# region # python包
from widget import Ui_window_debug, Ui_window_main, Ui_widget_config, Ui_widget_connect, Ui_widget_tags, Ui_widget_log, Ui_widget_clean, Ui_widget_ctu, \
    Ui_widget_leds, Ui_widget_imu, Ui_widget_graphConfig, Ui_widget_mecanum, Ui_widget_servers, Ui_window_tool_convertMsg, Ui_window_handle, Ui_widget_test, \
    Ui_widget_mqtt, Ui_widget_tcs_watch, Ui_widget_log_analyse, Ui_widget_OTA, Ui_widget_standly, Ui_widget_wifi, Ui_widget_license, Ui_widget_clean_driver, \
    Ui_widget_IO, Ui_widget_audio, Ui_widget_test_board, Ui_widget_camera, Ui_widget_map
import ast
import math
import serial
import re
import json
import struct
import threading
import socket
import os
import sys
import base64
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES
import openpyxl
from PyQt5.QtSerialPort import QSerialPortInfo
from pynput import keyboard
import numpy as np
from datetime import datetime
import pyqtgraph as pg
from PyQt5.QtCore import QThread, pyqtSignal, QObject, Qt, QTimer, QTime, QDateTime
from PyQt5.QtWidgets import QApplication, QMainWindow, QColorDialog, QMessageBox, QPushButton, QProgressBar, QHBoxLayout, QVBoxLayout, QWidget, QLabel, QLineEdit, QComboBox, QTabBar, QGridLayout, QCheckBox, QFileDialog, QRadioButton, QButtonGroup, qApp
from PyQt5.QtGui import QImage, QPixmap, QPalette, QBrush, QPainter, QColor, QPen
from PyQt5 import QtCore
import time
from paho.mqtt import client as mqtt_client
start_time = time.time()
print('开始导入包')
# 获取当前文件的绝对路径
current_file_path = os.path.abspath(__file__)
# 获取当前文件所在的目录
current_dir = os.path.dirname(current_file_path)
# 获取上级目录
parent_dir = os.path.dirname(current_dir)
sys.path.append(parent_dir)

# 加密解密相关

end_time = time.time()
elapsed_time = end_time - start_time
print("完成导入包", elapsed_time, "秒")
# endregion
# region # 定义全局变量
global_flag_client = 0  # socket连接标志位
global_s = 0  # socket，便于程序关闭时自动断开socket


# endregion
# region # 自定义函数
def makdir(directory):
    if not os.path.exists(directory):
        os.makedirs(directory)
        print(directory + "不存在,已创建")

    # else: print(directory+"已存在")


def extract_ip():  # 获取本机IP
    st = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        st.connect(('10.255.255.255', 1))
        IP = st.getsockname()[0]
    except Exception:
        IP = '127.0.0.1'
    finally:
        st.close()
    return IP


def get_time():  # 获取时间
    if flag_recordTime:
        return RecordTime
    else:
        current_time = datetime.now().strftime("%H:%M:%S.%f")[:-3]
        return current_time


def HexStrAddSpace(HexStr):  # 十六进制字符串标准化打印
    out_data = ''
    for i, s in enumerate(HexStr):
        if ((i % 2 == 0) & (i > 0)):
            out_data += ' '
            out_data += s
        else:
            out_data += s
    return out_data.upper()


def listToFloat(list):  # 字符列表转小数
    return round(struct.unpack('<f', struct.pack('4B', *list))[0],
                 5)  # <表示小端字节序 f：这指定了要解析的数据类型为单精度浮点数（float）'4'：这表示接下来的格式字符将被重复四次。'B'：这是单个格式字符，表示无符号字符（unsigned char）。

    # return round(struct.unpack('>f', struct.pack('4B', *list))[0],5)


def listToFloat_B(list):  # 字符列表转小数
    # return round(struct.unpack('<f', struct.pack('4B', *list))[0],5)#<表示小端字节序 f：这指定了要解析的数据类型为单精度浮点数（float）'4'：这表示接下来的格式字符将被重复四次。'B'：这是单个格式字符，表示无符号字符（unsigned char）。
    return round(struct.unpack('>f', struct.pack('4B', *list))[0], 5)


def floatToList(float_value):  # 小数转字符列表
    # 将浮点数转换为二进制表示
    # binary = struct.pack('!f', float_value)#参数'!f'表示使用大端字节序
    binary = struct.pack('f', float_value)
    # 将二进制表示转换为十六进
    hex_array = [int(b) for b in binary]  # int整数表示  hex字符表示
    return hex_array


def floatToList_B(float_value):  # 小数转字符列表_大端
    # 将浮点数转换为二进制表示
    binary = struct.pack('!f', float_value)  # 参数'!f'表示使用大端字节序
    # binary = struct.pack('f', float_value)
    # 将二进制表示转换为十六进
    hex_array = [int(b) for b in binary]  # int整数表示  hex字符表示
    return hex_array


def listToUint16(list):  # 字符列表转无符号32位整数
    return list[1] << 8 | list[0]


def listToInt16_big(list):  # 字符列表转有符号32位整数
    if list[0] >> 7:
        return (list[0] << 8 | list[1]) - 0x10000
    else:
        return list[0] << 8 | list[1]


def listToInt16(list):  # 字符列表转有符号32位整数
    if list[1] >> 7:
        return (list[1] << 8 | list[0]) - 0x10000
    else:
        return list[1] << 8 | list[0]


def listToInt32(arr):  # 将数组转为32位带符号的整数
    # byte_obj = bytes(arr)
    # num = int.from_bytes(byte_obj, 'little', signed=True)
    num = int.from_bytes(arr, 'big', signed=True)
    return num


def listToUint32(arr):  # 将数组转为32位无符号的整数
    num = int.from_bytes(arr, 'big', signed=False)
    return num


def listToInt32_little(arr):  # 将数组转为32位带符号的整数
    # byte_obj = bytes(arr)
    # num = int.from_bytes(byte_obj, 'little', signed=True)
    num = int.from_bytes(arr, 'little', signed=True)
    return num


def int32Tolist_big(num):
    return [num >> 24 & 0xff, num >> 16 & 0xff, num >> 8 & 0xff, num & 0xff]


def int32Tolist(num):
    return [num & 0xff, num >> 8 & 0xff, num >> 16 & 0xff, num >> 24 & 0xff]


def int16Tolist_big(num):
    return [num >> 8 & 0xff, num & 0xff]


def int16Tolist(num):
    return [num & 0xff, num >> 8 & 0xff]


def SumCheck(data, length):  # 按字节计算校验和
    checksum = 0
    for i in range(0, length):
        checksum += data[i]
        checksum &= 0xFF  # 强制截断
    return checksum


def clip_float(value, lower_bound, upper_bound):
    """
    将单个浮点数限制在 [lower_bound, upper_bound] 范围内。

    参数：
    - value: 需要限幅的浮点数。
    - lower_bound: 下限值。
    - upper_bound: 上限值。

    返回：
    - 限幅后的浮点数。
    """
    if value < lower_bound:
        return lower_bound
    elif value > upper_bound:
        return upper_bound
    else:
        return value


def get_exe_directory():
    # 检查是否是被打包后的环境
    if getattr(sys, 'frozen', False):
        # 如果是被打包后的环境，使用 sys.executable 获取可执行文件路径
        exe_path = sys.executable
    else:
        # 如果不是被打包后的环境，使用 __file__ 获取脚本路径
        exe_path = os.path.abspath(__file__)

    # 获取可执行文件所在的目录
    exe_directory = os.path.dirname(exe_path)
    return exe_directory


# endregion
# region # 自定义类
class QSSLoader:

    def __init__(self):
        pass

    @staticmethod
    def read_qss_file(qss_file_name):
        with open(qss_file_name, 'r', encoding='UTF-8') as file:
            return file.read()


class DeviceStatusChecker(QWidget):  # 设备离线监测
    update_deviceStatus = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.last_heartbeat_time = QTime.currentTime()
        self.heartbeat_timer = QTimer()
        self.heartbeat_timer.timeout.connect(self.check_heartbeat)
        self.heartbeat_timer.start(1000)  # 1秒

    def check_heartbeat(self):
        current_time = QTime.currentTime()
        elapsed_time = self.last_heartbeat_time.msecsTo(current_time)
        # print(elapsed_time)
        if elapsed_time > 333:
            self.update_deviceStatus.emit("离线")
        else:
            self.update_deviceStatus.emit("在线")


class MyComboBox(QComboBox):  # 重写下拉框点(击时获取串口列表)

    def __init__(self, parent=None):
        super().__init__(parent)

    def showPopup(self):
        name = self.currentText()
        self.clear()
        serials = QSerialPortInfo.availablePorts()
        for com in serials:
            self.addItem(com.portName() + ' ' + com.description())
        super().showPopup()
        index = self.findText(name)
        if index >= 0:
            self.setCurrentIndex(index)


class sig_paintRoute(QObject):  # 定义信号
    sig_point = pyqtSignal(float, float)


class signalclass(QObject):  # 定义信号
    print = pyqtSignal(str)
    data_process = pyqtSignal(bytes)
    process_PGVData = pyqtSignal(bytes)
    # update_time=pyqtSignal(str)
    show_msg = pyqtSignal(str)
    pgB_DmCode = pyqtSignal(int)


class thread_debug(QThread):  # 监视AGV参数及绘制曲线
    sig_updateData = pyqtSignal(list)
    sig_updateData2 = pyqtSignal(list)
    sig_updateGraph = pyqtSignal(list, list)
    dataNum = 100  # 曲线横坐标数据个数
    x = np.linspace(0, 100, dataNum)  # x轴数据

    p = [0] * 25
    p2 = [0] * 19
    # y1,y2,y3,y4,y5,y6=[0] * dataNum,[0] * dataNum,[0] * dataNum,[0] * dataNum,[0] * dataNum,[0] * dataNum
    graph_list = [[0] * 100 for _ in range(25)]

    def run(self):  # 该类模拟后台进程更新时间
        while True:
            time.sleep(1)

    def fuc_updateData1(self, agv_data):
        # print(len(agv_data))
        for i in range(25):
            if i == 13 or i == 14 or i == 24:  # 以int型解析
                datalist = agv_data[4 * i:4 * i + 4]
                self.p[i] = listToInt32(datalist)
            else:
                datalist = [agv_data[4 * i + 3], agv_data[4 * i + 2],
                            agv_data[4 * i + 1], agv_data[4 * i]]
                self.p[i] = listToFloat(datalist)
            self.graph_list[i][:-1] = self.graph_list[i][1:]  # 数据向前移动一个位置
            self.graph_list[i][-1] = self.p[i]
            # self.p[i]=listToFloat(agv_data[4*i:4*i+4].reverse() )
        # agv_data=agv_data[self.num_4B*4:]
        # for i in range(self.num_2B):
        #     self.p[i+self.num_4B]=listToUint16(agv_data[2*i:2*i+2])
        # agv_data=agv_data[self.num_2B*2:]
        # for i in range(self.num_1B):
        #     self.p[i+self.num_4B+self.num_2B]=agv_data[i:i+1]

        self.sig_updateData.emit(self.p)
        self.sig_updateGraph.emit(self.x.tolist(), self.graph_list)

    def fuc_updateData2(self, agv_data):

        datalist = [agv_data[3], agv_data[2], agv_data[1], agv_data[0]]
        self.p2[0] = listToFloat(datalist)
        agv_data = agv_data[1 * 4:]  # 4个字节的参数有1个
        print(len(agv_data))
        for i in range(2):
            #     # self.p2[i+1]=listToUint16(agv_data[2*i:2*i+2])
            self.p2[i + 1] = agv_data[2 * i:2 * i + 2]
            # print(self.p2[i+1])
            print(agv_data[2 * i:2 * i + 2])
        agv_data = agv_data[2 * 2:]  # 2个字节的参数有2个
        # print(len(agv_data))
        for i in range(16):  # 1个字节的参数有16个
            self.p2[i + 1 + 2] = agv_data[i:i + 1][0]
        print(type(agv_data[6:6 + 1][0]))
        self.sig_updateData2.emit(self.p2)


class thread_mecanum(QThread):  # 监测麦克纳姆轮数据
    sig_updateData = pyqtSignal(list, list, list)
    x = np.linspace(0, 100, 100)  # x轴数据
    p = [0] * 4
    data_list = [[0] * 100 for _ in range(4)]

    def run(self):  # 该类模拟后台进程更新时间
        while True:
            time.sleep(1)

    def fuc_updateData(self, data):
        for i in range(4):
            self.p[i] = listToInt16_big(data[2 * i:2 * i + 2])
            self.data_list[i][:-1] = self.data_list[i][1:]  # 数据向前移动一个位置
            self.data_list[i][-1] = self.p[i]
        self.sig_updateData.emit(self.x.tolist(), self.data_list, self.p)


class thread_angle(QThread):  # 监测IMU原始数据
    sig_updateAngle = pyqtSignal(list, list)
    sig_updateImuRaw = pyqtSignal(list, list, list)
    dataNum = 100  # 曲线横坐标数据个数
    dataNum_current = 0  # 当前收到的第几个数据
    x = np.linspace(0, 100, dataNum)  # x轴数据
    y = np.random.rand(100)  # 随机生成一些初始数据
    # y1=y2=y3=y4=y5=y6=[0] * 100
    y = [0] * 6
    y1, y2, y3, y4, y5, y6 = [0] * dataNum, [0] * dataNum, [0] * \
        dataNum, [0] * dataNum, [0] * dataNum, [0] * dataNum
    imuData_list = [y1, y2, y3, y4, y5, y6]

    def run(self):  # 该类模拟后台进程更新时间
        while True:
            time.sleep(1)

    def fuc_updateAngle(self, agv_currentAngle):
        self.y[:-1] = self.y[1:]  # 数据向前移动一个位置
        self.y[-1] = agv_currentAngle

    def fuc_updateImuRaw(self, imuRawData):
        average, p = [0] * 6, [0] * 6
        if (self.dataNum_current >= self.dataNum):
            self.dataNum_current = self.dataNum - 1
            for i in range(6):
                # gyroX,gyroY,gyroZ,accelX,accelY,accelZ
                p[i] = listToInt16(imuRawData[2 * i:2 * i + 2])
                # 数据向前移动一个位置
                self.imuData_list[i][:-1] = self.imuData_list[i][1:]
                self.imuData_list[i][-1] = p[i]
        else:
            for i in range(6):
                # gyroX,gyroY,gyroZ,accelX,accelY,accelZ
                p[i] = listToInt16(imuRawData[2 * i:2 * i + 2])
                self.imuData_list[i][self.dataNum_current] = p[i]

        self.dataNum_current += 1
        for i in range(6):
            self.y[i] = self.imuData_list[i][:(self.dataNum_current)]
            average[i] = np.mean(self.y[i])
            average[i] = np.round(average[i], 2)
        self.sig_updateImuRaw.emit(
            self.x[:self.dataNum_current].tolist(), self.y, average)

        # self.sig_updateImuRaw.emit(self.x.tolist(), self.imuData_list,average)


class thread_position(QThread):  # 定义进程
    update_position = pyqtSignal(list)
    last_new_point_x = 0
    last_new_point_y = 0
    dotdata = []

    def run(self):  # 该类模拟后台进程更新时间
        while True:
            time.sleep(0.1)

    def update(self, new_point_x, new_point_y):
        if ((self.last_new_point_x != new_point_x) | (self.last_new_point_y != new_point_y)):
            newpoint = {'pos': (new_point_x, new_point_y)}
            if newpoint not in self.dotdata:  # 点不存在则添加
                self.dotdata.append(newpoint)
            else:
                print("this point is exist")
            self.update_position.emit(self.dotdata)
        self.last_new_point_x = new_point_x
        self.last_new_point_y = new_point_y

    def clear(self):
        self.dotdata = []
        self.update_position.emit(self.dotdata)


class thread_feedback(QThread):  # 定义进程
    update_informaton = pyqtSignal(str, str, str, str)
    update_PGV_informaton = pyqtSignal(str, str, str, str)

    def run(self):  # 该类模拟后台进程更新时间
        while True:
            time.sleep(0.1)

    def update(self, data):
        if ((data[0] == 0x40) & (data[1] == 0x01)):  # 心跳包
            data = data[2:]
            agv_currentPosX = str(listToFloat(data[16:20]))  # 世界坐标X信息
            agv_currentPosY = str(listToFloat(data[20:24]))  # 世界坐标Y信息
            agv_currentAngle = str(listToFloat(data[24:28]))  # 角度信息
            agv_ultrasonic = str(listToFloat(data[28:32]))  # 超声波信息
            self.update_informaton.emit(
                agv_currentPosX, agv_currentPosY, agv_currentAngle, agv_ultrasonic)
        elif ((data[0] == 0x40) & (data[1] == 0x05)):  # 更新PGV相机信息
            data = data[2:]
            # x
            PGV_X_bias = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]
            if (data[0] >> 7):
                PGV_X_bias = PGV_X_bias - 0x100000000
            # y
            PGV_Y_bias = data[4] << 8 | data[5]
            if (data[4] >> 7):
                PGV_Y_bias = PGV_Y_bias - 0x10000
            # w
            PGV_Yaw = data[6] << 8 | data[7]
            if (data[6] >> 7):
                PGV_Yaw = PGV_Yaw - 0x10000
            PGV_Yaw = round(PGV_Yaw / 10, 1)
            # number
            PGV_tagNuber = data[8] << 24 & 0xff | data[9] << 16 & 0xff | data[10] << 8 & 0xff | data[11] & 0xff
            self.update_PGV_informaton.emit(str(PGV_X_bias), str(
                PGV_Y_bias), str(PGV_Yaw), str(PGV_tagNuber))


class thread_sendAudioFile(QThread):  # 进程_分包发送语音文件
    sig_sendAudioFile = pyqtSignal(list)
    sig_log = pyqtSignal(str)
    sig_next = pyqtSignal()
    sig_done = pyqtSignal()
    sig_packNum = pyqtSignal(int)

    def __init__(self, filename, index, next):
        super().__init__()
        self.filename = filename
        self.index = index
        self.next = next
        self.device_connect = 1

    def stop_send(self):
        self.device_connect = 0
        print("停止发送")

    def run(self):
        if self.filename:
            # print(self.filename)
            # print(self.index)
            with open(self.filename, 'rb') as f:
                data = f.read()
            fileLen = len(data)
            packNum = fileLen // 256 + 1
            self.sig_packNum.emit(packNum)
            leftLen = fileLen % 256
            # self.sig_log.emit("文件大小为%d个字节,%d个包,剩余%d个字节"%(fileLen,packNum,leftLen))
            frame_begin = [0x7E, 0xA0, 0x00, 0x03, self.index,
                           packNum // 256, packNum % 256, 0xEF]
            if self.device_connect:
                self.sig_sendAudioFile.emit(frame_begin)
                print("开始下载第%d个文件" % self.index)
            else:
                print("结束发送进程")
            time.sleep(0.3)
            if self.device_connect:
                for i in range(1, packNum):
                    frame_data = [0x7E, 0xA1, i // 256, i % 256] + \
                        list(data[256 * (i - 1):256 * i]) + [0xEF]
                    if self.device_connect:
                        self.sig_sendAudioFile.emit(frame_data)
                    else:
                        break
                    time.sleep(0.06)
                frame_data = [0x7E, 0xA1, packNum // 256, packNum % 256] + list(data[256 * (packNum - 1):256 *
                                                                                     (packNum - 1) + leftLen]) + [0x00] * (256 - leftLen) + [0xEF]
                if self.device_connect:
                    self.sig_sendAudioFile.emit(frame_data)
                time.sleep(0.3)
                frame_end = [0x7E, 0xA3, 0x00, 0x02, 0x00, self.index, 0xEF]
                if self.device_connect:
                    self.sig_sendAudioFile.emit(frame_end)
                time.sleep(0.1)
                if self.next:
                    self.sig_next.emit()
                else:
                    self.sig_done.emit()
            else:
                print("结束发送进程")


class thread_sendBin(QThread):  # 进程_分包发送升级文件
    sig_sendBin = pyqtSignal(bytes)

    def __init__(self, app_address):
        super().__init__()
        self.app_address = app_address

    def run(self):
        self.iap_filename, _ = QFileDialog.getOpenFileName(
            None, 'Open File', self.app_address)
        with open(self.iap_filename, 'rb') as f:
            data = f.read()
        self.fileLen = len(data)
        packLen = 1024
        packNum = self.fileLen // packLen
        for i in range(packNum):
            sendData = data[i * packLen:(i + 1) * packLen]
            sendData2 = bytes(sendData)
            if i != 0 and i % 30 == 0:
                time.sleep(2)  # 发送30K等待单片机存储到flash再发送
            self.sig_sendBin.emit(sendData2)
            time.sleep(0.2)
        if self.fileLen % packLen != 0:
            sendData = data[packNum * packLen:packNum *
                            packLen + self.fileLen % packLen]
            sendData2 = bytes(sendData)
            self.sig_sendBin.emit(sendData2)
        print('发送完成')


class PaintRoute(QWidget):  # 运动轨迹绘制

    def __init__(self, parent=None):
        super().__init__(parent)
        self.sig_paintRoute = sig_paintRoute()
        # 添加布局
        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.pw = pg.PlotWidget(title="AGV 运动轨迹", background='w')
        self.layout.addWidget(self.pw)
        # self.scatter = pg.ScatterPlotItem(size=10, brush= pg.mkBrush('g'),pen=pg.mkPen(color='g'))
        self.scatter = pg.ScatterPlotItem(
            size=10,
            brush=pg.mkBrush('g'),
            pen=pg.mkPen('g'),
            # pxMode=False,
            hoverable=True,
            hoverPen=pg.mkPen('g'),
            hoverBrush=pg.mkBrush('r'),
            hoverSize=10)
        self.pw.addItem(self.scatter)
        # 设置图表
        self.pw.setXRange(-3, 3)
        self.pw.setYRange(-3, 3)
        self.pw.showGrid(x=True, y=True, alpha=0.5)

        # 绘画示例
        self.dotdata = []  # 设置每个点的颜色
        # self.scatter.setData(self.dotdata)

        # self.pw.plot([5],[5],pen='g', brush=pg.mkBrush('r'),symbol='o')
        # self.pw.plot([1,2],[1,2],pen='b')
        # baseLine = pg.PolyLineROI([[0, 0], [1, 0], [1.5, 1], [2, 0], [3, 0]], pen=(0, 255, 0, 100), movable=False)
        # self.pw.addItem(baseLine)

        # 创建一个曲线项
        self.plot = self.pw.plot(
            symbol='o',
            pen='r',
        )
        # 初始化数据
        # self.data = np.random.rand(100)  # 随机生成一些初始数据
        # self.x = np.linspace(0, 10, 100)  # x轴数据
        self.x = []
        self.y = []
        # 更新数据并绘制曲线
        # self.update_plot()

        # 鼠标事件
        self.setMouseTracking(False)
        self.plot.scene().sigMouseMoved.connect(self.mouseover)
        # self.plot.scene().sigMouseClicked.connect(self.mouse_clicked)
        self.scatter.sigClicked.connect(self.clicked)
        # Make all plots clickable
        self.clickedPen = pg.mkPen('red', width=5)
        self.brush = pg.mkBrush(255, 0, 0, 120)
        self.lastClicked = []

    def clicked(self, plot, points):

        # for p in self.lastClicked:
        #     p.resetPen()
        # print("clicked points", p.pos())
        for p in points:
            p.setPen(self.clickedPen)
            p.setBrush(self.brush)
            print(p.data())
        self.lastClicked = points

    def update_plot(self):
        # 更新数据
        # self.data[:-1] = self.data[1:]  # 数据向前移动一个位置
        # self.data[-1] = np.random.rand()  # 在最后一个位置生成随机数

        # 更新曲线的数据
        self.plot.setData(self.x, self.data)

        # 设置曲线在x轴上的范围
        # self.pw.setXRange(self.x[0], self.x[-1])

        # 更新绘图
        self.pw.update()

        # 每隔一段时间调用update_plot函数来更新数据和绘图
        pg.QtCore.QTimer.singleShot(100, self.update_plot)

    def mouseover(self, pos):
        act_pos = self.plot.mapFromScene(pos)  # 参数pos 是像素坐标，需要 转化为  刻度坐标
        if type(act_pos) != QtCore.QPointF:
            return
        # print("over_1:",act_pos.x(), act_pos.y())
        self.new_point_x = round(act_pos.x(), 2)
        self.new_point_y = round(act_pos.y(), 2)

    def mouse_clicked(self, event):
        # if self.new_point_x not in self.x:
        #     self.x.append(self.new_point_x)
        # else:
        #     index = self.x.index(self.new_point_x)
        # if self.new_point_y not in self.y:
        #     self.y.append(self.new_point_y)

        # self.scatter.setData(self.dotdata)
        self.sig_paintRoute.sig_point.emit(self.new_point_x, self.new_point_y)


class AngleCurve(QWidget):  # 角度曲线绘制

    def __init__(self, parent=None):
        super().__init__(parent)
        # 添加布局
        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.pw = pg.PlotWidget(title="AGV 角度曲线", background='w')
        self.pw.showGrid(x=True, y=True, alpha=0.5)
        self.layout.addWidget(self.pw)

        self.plot = self.pw.plot(
            symbol='o',
            symbolBrush='r',
            pen='r',
        )
        self.x = np.linspace(0, 10, 100)  # x轴数据
        self.y = np.random.rand(100)  # 随机生成一些初始数据
        self.plot.setData(self.x, self.y)
        self.plot1 = self.pw.plot(
            symbol='h',
            symbolBrush='b',
            pen='b',
        )
        self.y1 = [item + 2 for item in self.y]
        self.plot1.setData(self.x, self.y1)


class Inputor(QObject):  # 输入指示灯
    INACTIVE = 0
    ACTIVE = 1

    def __init__(self, obj):
        super(Inputor, self).__init__()
        self._object = obj
        # print(obj)
        # self._size = obj.height()
        # print(self._size)
        self.inactive()

    def show(self, state):
        self._state = state
        colors = ['green', 'grey']  # 低电平即0时代表有输入
        self._object.setStyleSheet(
            "QPushButton{background-color:%s;border:0;border-radius: %dpx;}" % (colors[state], 16 / 2))

    def active(self):
        self._object.setStyleSheet(
            "QPushButton{background-color:%s;border:0;border-radius: %dpx;}" % ('green', self._size / 2))

    def inactive(self):
        self._object.setStyleSheet(
            "QPushButton{background-color:%s;border:0;border-radius: %dpx;}" % ('grey', 16 / 2))


class Indicator(QObject):  # 指示灯
    """
        指示灯，支持显示指定的状态，或闪烁显示
    """
    INACTIVE = 0
    WARNING = 1
    ACTIVE = 2
    DISMISS = 3

    def __init__(self, obj, reverse=False):
        super(Indicator, self).__init__()
        self._object = obj
        self._size = obj.minimumHeight()
        self._timer = QTimer()
        self._timer.timeout.connect(self.__flashHandle)
        self._state = self.INACTIVE
        self._flashBaseState = self.DISMISS
        self._flashState = self.INACTIVE
        self._reverse = reverse
        self.show(self.INACTIVE)

    def show(self, state):
        self._state = state
        colors = ['red', 'orange', 'green', "rgb(240,240,240)"]
        if self._reverse:
            colors = ['green', 'orange', 'red', "rgb(240,240,240)"]
        self._object.setStyleSheet(
            "QPushButton{background-color:%s;border:0;border-radius: %dpx;}" % (colors[state], self._size / 2))

    def active(self):
        self._timer.stop()
        self.show(self.ACTIVE)

    def inactive(self):
        self._timer.stop()
        self.show(self.INACTIVE)

    def warning(self):
        self._timer.stop()
        self.show(self.WARNING)

    def dismiss(self):
        self._timer.stop()
        self.show(self.DISMISS)

    def flash(self, state, period, baseState=DISMISS):
        if state == baseState:
            return
        self._flashState = state
        self._flashBaseState = baseState
        self.show(state)
        self._timer.start(period)

    def __flashHandle(self):
        if self._state == self._flashBaseState:
            self.show(self._flashState)
        else:
            self.show(self._flashBaseState)


class CustomRectWidget(QWidget):

    def __init__(self, shape):
        super().__init__()
        self.color_range = (0.3, 0.7)  # 初始颜色变化范围（20%到70%）
        self.setGeometry(50, 50, 300, 50)  # 设置位置和大小
        self.shape = shape
        if self.shape == "Horizontal":
            self.setMinimumSize(200, 20)
            self.setMaximumSize(200, 20)
        elif self.shape == "Vertical":
            self.setMinimumSize(20, 200)
            self.setMaximumSize(20, 200)

    def paintEvent(self, event):
        super().paintEvent(event)
        painter = QPainter(self)

        # 绘制整个矩形的背景色
        painter.setBrush(QBrush(QColor(220, 220, 220)))  # 浅灰色
        pen = QPen(QColor(200, 200, 200), 2)  # 黑色边框，宽度为 2
        painter.setPen(pen)
        print(self.rect())
        painter.drawRect(self.rect())

        # 计算颜色变化区域的宽度
        width = self.width()
        start_x = int(width * self.color_range[0])
        end_x = int(width * self.color_range[1])

        # 计算颜色变化区域的宽度
        height = self.height()
        start_y = int(height * self.color_range[0])
        end_y = int(height * self.color_range[1])

        # 绘制颜色变化区域
        if self.shape == "Horizontal":
            painter.setBrush(QBrush(QColor(0, 128, 0)))  # 绿色
            painter.setPen(QPen(QColor(0, 128, 0), 2))
            painter.drawRect(start_x, 0, end_x - start_x, self.height())
        elif self.shape == "Vertical":
            painter.setBrush(QBrush(QColor(0, 128, 0)))  # 绿色
            painter.setPen(QPen(QColor(0, 128, 0), 2))
            painter.drawRect(0, start_y, self.width(), end_y - start_y)

    def setColorRange(self, start, end):
        # 更新颜色变化范围并重新绘制控件
        self.color_range = (start, end)
        self.update()  # 触发重绘


class clientThread(QThread):
    # 定义信号来发送接收到的数据
    data_received = pyqtSignal(tuple, int, bytes)
    log = pyqtSignal(str)
    disConnect = pyqtSignal(int, QThread)

    def __init__(self, id, server_address):
        super().__init__()
        self.server_address = server_address
        self.identify = server_address.getpeername()
        self.running = True
        self.id = id

    def run(self):
        # current_thread = threading.current_thread()  #获取线程对象、名称，便于从线程列表中移除
        # sender = self.sender()
        # print(f"Running in thread: {self}")
        print(self.id)
        while self.running:
            try:
                data = self.server_address.recv(1024)
                if data:
                    self.data_received.emit(self.identify, self.id, data)
            except:
                self.log.emit(str(self.identify) + "接收异常")
                break
        self.disConnect.emit(self.id, self)
        self.server_address.close()

    def stop(self):
        self.running = False
        self.log.emit(str(self.identify) + '断开连接')
        self.server_address.close()

    def send_message(self, message):
        if self.server_address and self.server_address.fileno() != -1:
            try:
                self.server_address.sendall(message)
            except:
                # self.log.emit(f"Error sending message to {str(self.identify)}")
                self.log.emit(str(self.identify) + "已断开连接")
                self.disConnect.emit(self.id, self)
                self.server_address.close()
        else:
            self.log.emit(str(self.identify) + "连接不存在")

            # self.disConnect.emit(self.server_address)


class SocketWorker(QThread):
    # 定义信号来发送接收到的数据
    data_received = pyqtSignal(str)
    log = pyqtSignal(str)

    def __init__(self, server_address, client_socket=None):
        super().__init__()
        self.server_address = server_address
        self.client_socket = client_socket
        self.running = True

    def run(self):
        print('hlly')
        if not self.client_socket:
            try:
                self.client_socket = socket.socket(
                    socket.AF_INET, socket.SOCK_STREAM)
                self.client_socket.connect(self.server_address)
                self.log.emit(str(self.server_address) + '连接成功')
                while self.running:
                    try:
                        data = self.client_socket.recv(1024).decode()
                        if data:
                            self.data_received.emit(data)
                    # except ConnectionResetError:
                    except:
                        print("Connection lost")
                        break
            except socket.error as e:
                self.log.emit(str(self.server_address) + str(e))
        self.client_socket.close()

    def send_message(self, message):
        if self.client_socket and self.client_socket.fileno() != -1:
            # if self.client_socket:
            try:
                self.client_socket.sendall(message.encode())
            except:
                print(f"Error sending message to {self.server_address}")
                # self.client_socket.fileno()
                # self.running = False
        else:
            print('连接已断开')

    def stop(self):
        self.running = False
        print('断开连接')
        # self.wait()


class MqttThread(QThread):
    sig_connect_fail = pyqtSignal()
    mqtt_log = pyqtSignal(str)
    sig_data_process = pyqtSignal(str, bytes)
    signal_send_data = pyqtSignal(bytes)
    received_data = pyqtSignal(tuple, str)
    update_clients_list = pyqtSignal(str, tuple)
    stop_signal = pyqtSignal()
    sig_controlDevice_change = pyqtSignal(str)

    def __init__(self, ip, port, id):
        super().__init__()
        self.ip = ip
        self.port = port
        self.id = id
        self.client_sockets = []
        self.controlDevice = ''
        self.signal_send_data.connect(self.send_data)
        self.sig_controlDevice_change.connect(self.controlDevice_change)

    def run(self):
        self.client = mqtt_client.Client(
            mqtt_client.CallbackAPIVersion.VERSION2, self.id)
        # print('self.id:',self.id)
        # self.client = mqtt_client.Client(str(uuid.uuid4()))
        self.client.on_connect = self.on_connect    # 返回连接状态的回调函数
        self.client.on_message = self.on_message   # 返回订阅消息回调函数
        self.client.username_pw_set("theyn", "password")  # mqtt服务器账号密码
        try:
            self.client.connect(self.ip, self.port, 60)  # 连接mqtt服务器
            self.client.loop_start()
            self.client.subscribe("Test/Topic1")  # 订阅主题
            self.flag_run = True
            self.mqtt_log.emit("已连接MQTT服务器")
            while self.flag_run:
                time.sleep(1)
                # print("服务器正在运行...")
            self.client.disconnect()
            self.mqtt_log.emit("已断开MQTT服务器")
        except Exception as e:
            self.mqtt_log.emit("连接失败"+str(e))
            self.sig_connect_fail.emit()

    def stop(self):
        self.flag_run = False

    def send_data(self, data):
        for i in range(0, len(self.client_sockets)):
            try:
                self.client_sockets[i].send(data)
            except:
                self.client_sockets.remove(self.client_sockets[i])
                self.mqtt_log.emit(f"客户端 {self.client_sockets[i]} 已断开连接")

    def handle_client(self, client_socket, client_address):
        client_text = client_address[0]+':'+str(client_address[1])
        print(f"客户端 {client_text} 已连接")
        while self.flag_run:
            try:
                data = client_socket.recv(1024)
                if not data:
                    break
                if (client_text == self.controlDevice):  # 当前控制设备
                    self.mqtt_log.emit(data)
                else:  # 其他设备
                    try:
                        self.received_data.emit(
                            client_address, data.decode('utf-8'))
                    except Exception as e:
                        print(e)
            except Exception as e:
                print(f"客户端 {client_address} 断开连接，原因： {e}")
                break
        client_socket.close()
        self.mqtt_log.emit(f"客户端 {client_address} 断开连接")
        self.client_sockets.remove(client_socket)
        self.update_clients_list.emit("remove", client_address)  # 更新界面列表

    def controlDevice_change(self, device):
        self.controlDevice = device
        print(self.controlDevice)

    def on_connect(self, client, userdata, flags, reason_code, properties):
        print(f"Connected with result code {reason_code}")
        # client.subscribe("test/topic")
        # client.publish("test/topic", "Hello MQTT!")

    def on_message(self, client, userdata, msg):
        print(userdata)
        print(f"{msg.topic} {msg.payload}")
        message = msg.topic + ': ' + msg.payload.decode("utf-8")
        self.mqtt_log.emit(message)
        self.sig_data_process.emit(msg.topic, msg.payload)

    def subscribe(self, topic):
        self.client.subscribe(topic)
        self.mqtt_log.emit('subscribed to topic '+topic)

    def unsubscribe(self, topic):
        self.client.unsubscribe(topic)
        self.mqtt_log.emit('unsubscribed from topic '+topic)

    def publish(self, topic, message, isShow):
        self.client.publish(topic, message)
        if isShow:
            self.mqtt_log.emit('published to topic ' +
                               topic+' message '+message)

# region #
# class VideoThread(QThread):
#     change_pixmap_signal = pyqtSignal(QImage)
#     update_DM = pyqtSignal(str,int,int,int,int)

#     def __init__(self,rtmp_url):
#         super().__init__()
#         self.rtmp_url = rtmp_url
#         self.cap = cv2.VideoCapture(self.rtmp_url)
#         self.timer0_decode = QTimer()
#         self.timer0_decode.timeout.connect(self.fuc_decode)
#         # self.timer0_decode.start(1000)
#         self.ret=False
#         self.x, self.y, self.w, self.h = 0,0,0,0

#         # 相机识别子二维码像素坐标 到 二维码矩阵坐标 转换
#         cam_width=320#相机像素宽
#         cam_height=240#相机像素高
#         len_codes=85#二维码矩阵长宽为85mm
#         len_code=15#子二维码长宽为15mm
#         len_gap=5#两个子二维码中间间隙宽为15mm
#         scale_camHeight=1#相机镜头到二维码的高度影响二维码长宽占实际像素的大小
#         scale_camToPos=len_codes/cam_height*scale_camHeight
#         scale_posToCam=cam_height/len_codes*scale_camHeight

#         rows = 4
#         cols = 4
#         code_dis = [[0 for _ in range(cols)] for _ in range(rows)]
#         code_angle = [[0 for _ in range(cols)] for _ in range(rows)]
#         code_x = [[0 for _ in range(cols)] for _ in range(rows)]
#         code_y = [[0 for _ in range(cols)] for _ in range(rows)]
#         dis_xy=[-1.5*len_gap+-2*len_code,-0.5*len_gap-len_code,0.5*len_gap,1.5*len_gap+len_code]#相机相对于二维码为0°时,实际角度为90°时,子二维码起点到原点的x,y距离
#         angle=0#实际角度为90°,相机相对于二维码为0°;实际角度90°→0°，相机相对于二维码为0°°→90°
#         for i in range(rows):
#             for j in range(cols):
#                 code_dis[i][j]=math.hypot(dis_xy[i],dis_xy[j])
#                 code_angle[i][j]=math.atan2(dis_xy[i],dis_xy[j])/math.pi*180
#                 # print("第{}行{}列，距离{}".format(i,j,code_dis[i][j]))
#                 # print("第{}行{}列，距离{}，角度{}".format(i,j,code_dis[i][j],code_angle[i][j]))

#     def run(self):
#         self.exiting = False
#         print('开启摄像头')
#         self.cap = cv2.VideoCapture(self.rtmp_url)
#         if not self.cap.isOpened():
#             print("摄像头编号不存在，请检查摄像头连接或选择正确的摄像头编号。")
#         else:

#             print("摄像头信息：")
#             print("摄像头分辨率：", self.cap.get(cv2.CAP_PROP_FRAME_WIDTH), "x", self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
#             print("摄像头帧率：", self.cap.get(cv2.CAP_PROP_FPS))
#             print("摄像头是否打开：", self.cap.isOpened())
#             # self.timer0_decode.start(1000)
#             th2 = threading.Thread(target=self.fuc_decode)
#             th2.start()
#             while True:
#                 if self.exiting:
#                     self.cap.release()
#                     break
#                 try:
#                     self.ret, self.frame = self.cap.read()
#                 except Exception as e:
#                     print("发生错误：", e)
#                     break
#                 if self.ret:
#                     rgb_image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)

#                     # 在图像上绘制矩形
#                     cv2.rectangle(rgb_image, (self.x, self.y), (self.x + self.w, self.y + self.h), (0, 255, 0), 2)
#                     h, w, ch = rgb_image.shape
#                     bytes_per_line = ch * w
#                     convert_to_qt_format = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
#                     p = convert_to_qt_format.scaled(320, 240, aspectRatioMode=QtCore.Qt.KeepAspectRatio)
#                     self.change_pixmap_signal.emit(p)
#                     # all_barcode_info = pylibdmtx.decode(convert_to_qt_format, timeout=500, max_count=1)
#                     # print(all_barcode_info)
#                     # time.sleep(1)
#                     # del(ret)
#                     # del(frame)

#                 # print("hlly")

#     def stop(self):
#         self.exiting = True
#         self.ret=False
#         # self.timer0_decode.stop()
#         self.quit()
#         self.wait()
#         print("线程退出")
#     def fuc_decode(self):
#         # 保存图像到文件
#         # ret, frame = self.cap.read()
#         # print(ret)
#         # cv2.imwrite("captured_image.jpg", self.frame)
#         # 加载图片
#         # image = cv2.imread('captured_image.jpg')


#         # 解析二维码
#         while not self.exiting:
#             if self.ret:
#                 t0 = time.time()
#                 all_barcode_info = pylibdmtx.decode(self.frame, timeout=500, max_count=1)
#                 print(time.time() - t0)
#                 # print(all_barcode_info)
#                 # print(len(all_barcode_info))
#                 if len(all_barcode_info) == 1:
#                     # print(all_barcode_info[0][0][0])
#                     # print(all_barcode_info[0][0][1])
#                     # print(all_barcode_info[0][0][2])
#                     row=all_barcode_info[0][0][1]-48-2
#                     col=all_barcode_info[0][0][0]-48-2
#                     print(row,col)
#                     # print(all_barcode_info[0][1][0])
#                     # print(all_barcode_info[0][1][1])
#                     # 定义矩形的左上角坐标、宽度和高度
#                     self.x, self.y, self.w, self.h = all_barcode_info[0][1][0], 240-all_barcode_info[0][1][1], all_barcode_info[0][1][2], -all_barcode_info[0][1][3]
#                     # print(type(str(all_barcode_info[0][0])))
#                     witdth=math.hypot(self.w, self.h)
#                     print(witdth)
#                     self.update_DM.emit(all_barcode_info[0][0].decode('gbk'), self.x, self.y, self.w, self.h)
#                     # print(len(all_barcode_info[0][1]))
#                     # print(type(all_barcode_info[0][1]))
#                 time.sleep(1)
# endregion
class ServerThread(QThread):
    received_data = pyqtSignal(tuple, str)
    server_log = pyqtSignal(str)
    update_clients_list = pyqtSignal(str, tuple)
    stop_signal = pyqtSignal()
    signal_send_data = pyqtSignal(bytes)
    sig_data_process = pyqtSignal(bytes)
    sig_controlDevice_change = pyqtSignal(str)

    def __init__(self, host, port):
        super().__init__()
        self.host = host
        self.port = port
        self.client_sockets = []
        self.controlDevice = ''
        self.signal_send_data.connect(self.send_data)
        self.sig_controlDevice_change.connect(self.controlDevice_change)

    def run(self):
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.server_socket.bind((self.host, self.port))
        self.server_socket.listen(5)
        self.flag_run = True
        self.server_log.emit("服务器已启动，等待客户端连接...")

        while self.flag_run:
            try:
                client_socket, client_address = self.server_socket.accept()
                client_socket.settimeout(7)  # 设置超时时间为3秒
                self.server_log.emit(f"客户端 {client_address} 已连接")
                self.client_sockets.append(client_socket)
                self.update_clients_list.emit("add", client_address)  # 更新界面列表
                threading.Thread(target=self.handle_client, args=(
                    client_socket, client_address)).start()
            except:
                self.server_log.emit("服务器已关闭")
                # client_socket.close()

    def stop(self):
        self.flag_run = False
        self.server_socket.close()
        # 关闭所有连接设备
        # for i in range(0,len(self.client_sockets)):
        #     try:
        #         self.client_sockets[i].send(b"exit")
        #         self.client_sockets[i].close()
        #     except:
        #         break
        # self.client_sockets=[]

    def send_data(self, data):
        for i in range(0, len(self.client_sockets)):
            try:
                self.client_sockets[i].send(data)
            except:
                self.client_sockets.remove(self.client_sockets[i])
                self.server_log.emit(f"客户端 {self.client_sockets[i]} 已断开连接")

    def handle_client(self, client_socket, client_address):
        client_text = client_address[0] + ':' + str(client_address[1])
        while self.flag_run:
            try:
                data = client_socket.recv(1024)
                if not data:
                    break
                if (client_text == self.controlDevice):  # 当前控制设备
                    self.sig_data_process.emit(data)
                else:  # 其他设备
                    try:
                        self.received_data.emit(
                            client_address, data.decode('utf-8'))
                    except Exception as e:
                        print(e)
            except Exception as e:
                print(f"客户端 {client_address} 断开连接，原因： {e}")
                break
        client_socket.close()
        self.server_log.emit(f"客户端 {client_address} 断开连接")
        self.client_sockets.remove(client_socket)
        self.update_clients_list.emit("remove", client_address)  # 更新界面列表

    def controlDevice_change(self, device):
        self.controlDevice = device
        print(self.controlDevice)


class thread_playData(QThread):  # 播放录制的数据
    send_data = pyqtSignal(bytes)
    sig_progress = pyqtSignal(int)

    def __init__(self):
        super().__init__()
        self.flag_run = True
        self.time_interval = 1
        self.datas = None
        self.time_last = None
        self.multiSpeed = 1
        self.jumpProgress = 0

    def run(self):
        global flag_recordTime, RecordTime
        flag_recordTime = True
        self.time_last = None
        for i in range(len(self.datas)):
            if i < self.jumpProgress:
                continue
            data = self.datas[i]
            self.sig_progress.emit(i+1)  # 更新进度条
            RecordTime = data['timestamp'].strftime("%H:%M:%S.%f")[:-3]  # 更新时间
            self.send_data.emit(data['data'])  # 更新数据
            # 延时
            if self.time_last:
                delta = data['timestamp'] - self.time_last
                time.sleep(delta.total_seconds() * self.multiSpeed)
            self.time_last = data['timestamp']
            while not self.flag_run:
                time.sleep(0.1)
        print('stop')

    def stop(self):
        self.flag_run = False
        self.wait()  # 等待线程真正结束


class thread_test(QThread):  # 定义进程
    update_text = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.flag_run = True
        self.time_interval = 3

    def run(self):
        while self.flag_run:
            self.update_text.emit('hlly')
            time.sleep(self.time_interval)

    def stop(self):
        self.flag_run = False
        self.wait()  # 等待线程真正结束

# endregion
# region # 自定义窗口


class Window_tool_convertMsg(QMainWindow, Ui_window_tool_convertMsg.Ui_MainWindow):  # 调度协议转换窗口

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.btn_convert.clicked.connect(self.fuc_convert)
        self.btn_clear.clicked.connect(self.fuc_clear)

    def fuc_convert(self):
        text = self.text_raw.toPlainText()
        arr = text.split(" ")
        msgHead = ''.join([chr(int(x, 16)) for x in arr[:24]])
        msgLen = int(msgHead[4:9])
        msgCmd = ''
        for j in range(10):
            cmd = arr[24 + 32 * j:24 + 32 + 32 * j]
            cmdNum = int(cmd[0], 16) << 8 | int(cmd[1], 16)
            if cmdNum:
                cmdName = ''.join([chr(int(x, 16)) for x in cmd[2:4]])
                cmd_p = []
                for i in range(7):
                    cmd_pi = listToInt32([int(x, 16)
                                         for x in cmd[4 * i + 4:4 * i + 8]])
                    cmd_p.append(cmd_pi)
                msgCmd += str(cmdNum) + " " + cmdName + " " + str(cmd_p)
        print(msgCmd)
        if self.check_showTime.isChecked():
            msgCmd = get_time() + "  " + msgCmd
        self.text_convert.append(msgCmd)

    def fuc_clear(self):
        self.text_convert.setText('')


class Window_debug(QMainWindow, Ui_window_debug.Ui_MainWindow):  # 详细参数监测窗口
    sig_close = pyqtSignal()

    def __init__(self):
        super().__init__()
        self.setupUi(self)

    def closeEvent(self, event):
        # 在这里添加你想要在关闭窗口时执行的操作
        self.sig_close.emit()

        # event.accept()


class Window_handle(QMainWindow, Ui_window_handle.Ui_MainWindow):  # 手柄

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_config(QWidget, Ui_widget_config.Ui_Form):  # 配置窗口

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_map(QWidget, Ui_widget_map.Ui_Form):  # 配置窗口

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_tags(QWidget, Ui_widget_tags.Ui_Form):  # 二维码标定

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_IO(QWidget, Ui_widget_IO.Ui_Form):  # IO输入输出

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_testBoard(QWidget, Ui_widget_test_board.Ui_Form):  # 控制板测试

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_clean(QWidget, Ui_widget_clean.Ui_Form):  # 清洗机

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_clean_driver(QWidget, Ui_widget_clean_driver.Ui_Form):  # 清洗机驱动板

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_OTA(QWidget, Ui_widget_OTA.Ui_Form):  # 远程升级

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_standly(QWidget, Ui_widget_standly.Ui_Form):  # standly纠偏算法测试

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_audio(QWidget, Ui_widget_audio.Ui_Form):  # 语音播报

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_wifi(QWidget, Ui_widget_wifi.Ui_Form):  # wifi参数

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_license(QWidget, Ui_widget_license.Ui_Form):  # license授权

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class w_test(QWidget, Ui_widget_test.Ui_Form):  # 测试

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_log_analyse(QWidget, Ui_widget_log_analyse.Ui_Form):  # 调度日志分析

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_tcs_watch(QWidget, Ui_widget_tcs_watch.Ui_Form):  # 调度监测

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_ctu_up(QWidget, Ui_widget_ctu.Ui_Form):  # CTU上装

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_leds(QWidget, Ui_widget_leds.Ui_Form):  # LED

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_log(QWidget, Ui_widget_log.Ui_Form):  # 日志窗口

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_connect(QWidget, Ui_widget_connect.Ui_Form):  # 连接窗口

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_graphConfig(QWidget, Ui_widget_graphConfig.Ui_Form):  # 曲线图配置

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class widget_mecanum(QWidget, Ui_widget_mecanum.Ui_Mecanum):  # 麦克纳姆轮小车调试

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.plot_widget = pg.GraphicsLayoutWidget()
        self.layout_plot.addWidget(self.plot_widget)
        self.plot_item1 = self.plot_widget.addPlot()
        self.plot_item1.addLegend(size=(150, 80))
        # self.plot_item1.showGrid(x=True, y=True, alpha=0.5)#网格透明度
        # symbol='o',,,gyroZ,accelX,accelY,accelZ
        self.curve_Va = self.plot_item1.plot(pen='r', name='Va')
        self.curve_Vb = self.plot_item1.plot(pen='g', name='Vb')  # 绿色 (Green)
        self.curve_Vc = self.plot_item1.plot(
            pen='b', name='Vc')  # ,symbol='o'蓝色 (Blue)
        self.curve_Vd = self.plot_item1.plot(
            pen='m', name='Vd')  # ,symbol='o'品红 (Magenta)
        self.curve = [self.curve_Va, self.curve_Vb,
                      self.curve_Vc, self.curve_Vd]


class widget_imu(QWidget, Ui_widget_imu.Ui_Form):  # IMU曲线

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.average = [
            self.input_average_gyroX, self.input_average_gyroY, self.input_average_gyroZ, self.input_average_accX, self.input_average_accY,
            self.input_average_accZ
        ]
        # 创建一个GraphicsLayoutWidget对象
        self.plot_widget = pg.GraphicsLayoutWidget()
        self.layout_plot.addWidget(self.plot_widget)

        # 在GraphicsLayoutWidget中添加一个PlotItem
        self.plot_item1 = self.plot_widget.addPlot()
        self.plot_widget.nextRow()
        self.plot_item2 = self.plot_widget.addPlot()

        # 生成数据
        self.x = np.linspace(0, 100, 100)
        self.y = 1000 * np.sin(self.x)
        self.y2 = 1000 * np.sin(self.x) + 2
        self.imudata = []
        # 绘制曲线图
        self.plot_item1.addLegend(size=(150, 80))
        self.plot_item2.addLegend(size=(150, 80))
        # self.plot_item1.showGrid(x=True, y=True, alpha=0.5)#网格透明度
        # symbol='o',,,gyroZ,accelX,accelY,accelZ
        self.curve_gyroX = self.plot_item1.plot(pen='r', name='gyroX')
        self.curve_gyroY = self.plot_item1.plot(
            pen='g', name='gyroY')  # 绿色 (Green)
        self.curve_gyroZ = self.plot_item1.plot(
            pen='b', name='gyroZ')  # ,symbol='o'蓝色 (Blue)
        self.curve_accX = self.plot_item2.plot(
            pen='c', name='accelX')  # 青色 (Cyan)
        self.curve_accY = self.plot_item2.plot(
            pen='m', name='accelY')  # 品红 (Magenta)
        self.curve_accZ = self.plot_item2.plot(
            pen='y', name='accelZ')  # 黄色 (Yellow)
        self.curve = [self.curve_gyroX, self.curve_gyroY, self.curve_gyroZ,
                      self.curve_accX, self.curve_accY, self.curve_accZ]


class widget_servers(QWidget, Ui_widget_servers.Ui_Form):  # 多服务器连接

    def __init__(self):
        super().__init__()
        self.setupUi(self)

    #     self.btn_connect.clicked.connect(self.connect_server)
    # def connect_server(self):
    #     print('hlly')


class w_mqtt(QWidget, Ui_widget_mqtt.Ui_Form):  # MQTT客户端

    def __init__(self):
        super().__init__()
        self.setupUi(self)


class window_picture(QMainWindow):  # 纯背景图片窗口

    def __init__(self):
        super().__init__()

        # self.setWindowOpacity(0.5)
        # 设置窗口无边框
        self.setWindowFlags(Qt.FramelessWindowHint)
        # 设置窗口尺寸为宽度400像素，高度300像素
        # self.setFixedSize(1787, 954)
        self.setFixedSize(1450, 860)
        # 设置窗口背景图片
        palette = QPalette()
        # pixmap = QPixmap(r'E:\python\board.png')
        pixmap = QPixmap(r'E:\python\background.jpg')
        pixmap = pixmap.scaled(self.size(), Qt.KeepAspectRatioByExpanding)
        brush = QBrush(pixmap)
        # 设置背景图片

        # self.setAutoFillBackground(True)
        palette.setBrush(QPalette.Background, brush)
        self.setPalette(palette)
        # # # 创建一个布局并将QLabel添加到布局中
        layout = QVBoxLayout()
        # # layout.addWidget(self.label)

        # # # 创建一个QWidget对象并将布局设置为其布局
        container = QWidget()
        container.setLayout(layout)

        # # # 将QWidget设置为主窗口的中心窗口
        self.setCentralWidget(container)


class widget_camera(QWidget, Ui_widget_camera.Ui_Form):  # 相机

    def __init__(self):
        super().__init__()
        self.setupUi(self)
# endregion


class Window_main(QMainWindow, Ui_window_main.Ui_MainWindow):  # 主窗口

    def __init__(self):
        super().__init__()
        self.setupUi(self)

        # self.setFixedSize(1450, 860)
        # self.setWindowFlag(QtCore.Qt.FramelessWindowHint) # 隐藏边框
        self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)  # 设置获取焦点的方法
        self.widget_connect = widget_connect()
        self.widget_log = widget_log()
        # region #
        # endregion
        # region # 按键监听线程
        self.keyIsPressed = False
        self.AppIsFocus = False
        listener = keyboard.Listener(on_press=self.on_press, on_release=self.on_release)
        listener.start()
        # endregion
        # region # 设置窗口大小
        size = self.size()
        width = size.width()
        height = size.height()
        # self.resize(1920, 1080)
        # self.showNormal()
        # self.resize(800, 600)
        # print(f'窗口宽度： {width}, 窗口高度： {height}')
        # endregion
        # region # 定义信号
        self.signal = signalclass()
        self.signal.show_msg[str].connect(self.sigCall_show_msg)
        self.signal.print[str].connect(self.sigCall_print)
        self.signal.pgB_DmCode.connect(self.sigCall_pgB_DmCode)
        self.signal.data_process.connect(self.fuc_data_process)
        # endregion
        # region # 定义进程
        self.thread_angle = thread_angle()
        self.thread_angle.sig_updateAngle.connect(self.sigCall_update_angle)
        self.thread_angle.sig_updateImuRaw.connect(self.sigCall_update_imuRawData)
        self.thread_angle.start()
        self.thread_position = thread_position()
        self.thread_position.update_position.connect(self.sigCall_update_position)
        self.thread_position.start()
        self.thread_feedback = thread_feedback()
        self.thread_feedback.update_informaton.connect(self.sigCall_update_infomation)
        self.thread_feedback.update_PGV_informaton.connect(self.sigCall_update_PGV_informaton)
        self.thread_feedback.start()
        self.thread_debug = thread_debug()
        self.thread_debug.sig_updateData.connect(self.sigCall_update_debug)
        self.thread_debug.sig_updateData2.connect(self.sigCall_update_debug2)
        self.thread_debug.sig_updateGraph.connect(self.sigCall_update_debugGraph)
        self.thread_debug.start()
        self.thread_mecanum = thread_mecanum()
        self.thread_mecanum.sig_updateData.connect(self.sigCall_update_mecanum)
        self.thread_mecanum.start()
        self.deviceStatusChecker = DeviceStatusChecker()
        self.deviceStatusChecker.update_deviceStatus.connect(self.sigCall_update_deviceStatus)
        # endregion
        # region # 定义定时器
        # self.timer0_deviceState = QTimer()
        # self.timer0_deviceState.timeout.connect(self.fuc_update_deviceState)
        # self.timer0_deviceState.start(1000)
        # self.timer1_agv_stop = QTimer()
        # self.timer1_agv_stop.timeout.connect(self.fuc_agv_autoStop)
        # self.timer2_agv_moveCycle = QTimer()
        # self.timer2_agv_moveCycle.timeout.connect(self.fuc_agv_cycleMove)
        # self.timer3_camera_query = QTimer()
        # self.timer3_camera_query.timeout.connect(self.fuc_camera_query)
        # self.timer4_imu_query = QTimer()
        # self.timer4_imu_query.timeout.connect(lambda:self.fuc_imu_config("queryAngle"))
        # self.timer5_agv_angleJudge = QTimer()
        # self.timer5_agv_angleJudge.timeout.connect(self.fuc_agv_angleJudge)
        # self.timer6_agv_odometer = QTimer()
        # self.timer6_agv_odometer.timeout.connect(self.fuc_agv_odometer)
        # self.timer7_agv_toDistance = QTimer()
        # self.timer7_agv_toDistance.timeout.connect(self.fuc_agv_toDistance)
        # self.timer8_agv_anologAngle = QTimer()
        # self.timer8_agv_anologAngle.timeout.connect(self.fuc_agv_anologAngle)
        self.timer9_playPoints = QTimer()
        self.timer9_playPoints.timeout.connect(self.task_playPoints)
        self.timer10_imuTest = QTimer()
        self.timer10_imuTest.timeout.connect(self.task_imuTest)
        # self.timer11_update_mapPoints = QTimer()
        # self.timer11_update_mapPoints.timeout.connect(self.task_update_mapPoints)
        # 创建一个定时器用于定时更新图像
        # self.timer12 = QTimer(self)
        # self.timer12.timeout.connect(self.update_frame)

        # self.timerTest = QTimer(self)
        # self.timerTest.timeout.connect(self.timer_Test)
        # self.timerTest.start(1000)

        self.timer13 = QTimer(self)
        self.timer13.timeout.connect(self.resizeWindow)
        self.timer13.start(50)  # 重置窗口大小
        self.timer14_decode = QTimer()
        self.timer14_decode.timeout.connect(self.fuc_decode)
        # self.timer14_decode.start(1000)
        # endregion
        # region # 默认配置页面
        # self.add_tab("人员权限")# 添加标签页
        # self.add_tab("设置")
        # self.tab_widget.removeTab(0)
        # self.tab_widget.addTab(self.tabBar, 'title')
        self.tab_config = QWidget()
        layout = QVBoxLayout()
        self.widget_config = widget_config()
        layout.addWidget(self.widget_config)
        self.tab_config.setLayout(layout)
        # self.tab_widget.addTab(self.tab_config, '启动参数配置')
        self.widget_config.btn_config_save.clicked.connect(self.fuc_config_save)
        self.widget_config.btn_config_load.clicked.connect(self.fuc_config_load)
        self.widget_config.btn_config_set.clicked.connect(self.fuc_config_set)
        self.widget_config.btn_config_restart.clicked.connect(self.restart)

        # self.fuc_log(f"当前 exe 所在文件夹路径: {parent_dir_path}")
        file_path = parent_dir_path + "/appDoc/config/default.txt"
        # self.fuc_log(file_path)

        if os.path.exists(file_path):
            # print('文件存在')
            with open(file_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
                for line in lines:
                    if '#' in line:
                        line = line[:line.index('#')]  # 去除每行#后的内容
                    if line.strip():  # 去除字符串两端的空白字符（包括空格、换行符和制表符）
                        parameter = line.strip().split(":", 1)
                        search_string = parameter[0]
                        found_widget = self.widget_config.findChild(QLineEdit, search_string)  # 根据字符串查找widget中的对象
                        if found_widget:
                            found_widget.setText(parameter[1])
                        else:
                            found_widget = self.widget_config.findChild(QComboBox, search_string)  # 根据字符串查找widget中的对象
                            if found_widget:
                                found_widget.setCurrentText(parameter[1])  # 必须是下拉列表中的已有项
                            else:
                                found_widget = self.widget_config.findChild(QCheckBox, search_string)
                                found_widget.setChecked(True if parameter[1] == 'True' else False)
        else:
            print('文件不存在')
            dir_path = parent_dir_path + "/appDoc/config"
            if not os.path.exists(dir_path):  # 创建目录
                os.makedirs(dir_path)
            with open(file_path, "w") as file:  # 创建文件
                pass

        file_path = parent_dir_path + "/appDoc/config/serverIpPort.txt"
        if os.path.exists(file_path):
            with open(file_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
            if lines == []:
                print('文件无内容')
            else:
                self.widget_connect.com_server.clear()
                for line in lines:
                    if '#' in line:
                        line = line[:line.index('#')]  # 去除每行#后的内容
                    if line.strip():  # 去除字符串两端的空白字符（包括空格、换行符和制表符）
                        Ip = line.strip()
                        # print(Ip)
                        self.widget_connect.com_server.addItem(Ip)
                        # parameter=line.strip().split(":", 1)
                        # self.widget_connect.com_server.addItem(parameter[0])
                # print(lines)
        else:
            print('文件不存在')
            dir_path = parent_dir_path + "/appDoc/config"
            if not os.path.exists(dir_path):  # 创建目录
                os.makedirs(dir_path)
            with open(file_path, "w") as file:  # 创建文件
                pass
        # endregion
        # region # 参数配置_上位机
        self.data_from_board = 1
        p_serverIP = self.widget_config.p_serverIP.text()
        p_serverPort = self.widget_config.p_serverPort.text()
        if p_serverIP == '':
            p_serverIP = "10.0.0.78"
        p_protocol = self.widget_config.p_protocol.currentText()
        if p_protocol == '':
            p_protocol = "My"
        p_page_index = int(self.widget_config.p_page_index.currentText().split(":")[0])

        self.widget_connect.com_protocol.setCurrentText(p_protocol)
        self.protocol = p_protocol  # 默认通讯协议
        self.widget_connect.com_server.setCurrentText(p_serverIP)
        self.widget_connect.comboBox_serverPort.setCurrentText(p_serverPort)
        self.stackedWidget.setCurrentIndex(p_page_index)  # 初始化显示第几个界面,[2:远程升级 6:调度]
        self.app_address = self.widget_config.p_address_bin.text()  # 远程升级文件地址
        if self.app_address == "":
            self.app_address = parent_dir_path

        p_lineSpeed = self.widget_config.p_lineSpeed.text()
        self.input_agv_lineSpeed.setText(p_lineSpeed)
        p_rateSpeed = self.widget_config.p_rateSpeed.text()
        self.input_agv_rateSpeed.setText(p_rateSpeed)
        self.widget_connect.com_protocol.currentIndexChanged.connect(self.change_protocol)
        self.widget_connect.com_protocol_1.currentIndexChanged.connect(self.change_protocol)
        self.cnt_deviceState = 0
        self.last_agv_currentPosX = 1
        self.last_agv_currentPosY = 1

        # self.mode_debug="客户端"
        self.mode_debug = self.widget_config.p_mode_debug.currentText()
        self.action_mode_uart.triggered.connect(self.change_debugMode)  # 选择调试模式
        self.action_mode_client.triggered.connect(self.change_debugMode)
        self.action_mode_server.triggered.connect(self.change_debugMode)
        self.action_config.triggered.connect(self.show_widget_config)
        if self.mode_debug != "串口":
            self.hide_all_widgets(self.widget_connect.gridLayout_com)
        if self.mode_debug != "服务器":
            self.hide_all_widgets(self.widget_connect.gridLayout_server)
        if self.mode_debug != "客户端":
            self.hide_all_widgets(self.widget_connect.gridLayout_client)
        # endregion
        # region # 打开摄像头
        self.widget_camera = widget_camera()
        self.flag_camera = 0
        self.disply_width = 640
        self.display_height = 480
        self.widget_camera.label_video.resize(self.disply_width, self.display_height)
        # self.video_thread = VideoThread('rtmp://127.0.0.1:1935/live/123')
        # self.video_thread = VideoThread('rtsp://127.0.0.1:8554/123')

        # self.video_thread = VideoThread(0)
        # self.video_thread.change_pixmap_signal.connect(self.update_image)
        # self.video_thread.update_DM.connect(self.update_DM)

        # self.cap = cv2.VideoCapture(1)
        # if not self.cap.isOpened():
        #     print("摄像头编号不存在，请检查摄像头连接或选择正确的摄像头编号。")
        # else:
        #     print("摄像头信息：")
        #     print("摄像头分辨率：", self.cap.get(cv2.CAP_PROP_FRAME_WIDTH), "x", self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
        #     print("摄像头帧率：", self.cap.get(cv2.CAP_PROP_FPS))
        #     print("摄像头是否打开：", self.cap.isOpened())
        # endregion
        # region # 子界面/界面显示切换
        # 分割器
        self.splitter.setSizes([100, 900])
        self.splitter_2.setSizes([300, 300])

        # TAB选项卡
        self.tabBar = self.tab_widget.findChild(QTabBar)
        # self.tabBar.hide()  #主页标签显示控制
        # self.tab_widget.tabBarDoubleClicked.connect(self.changeTabName) # 双击设置选项卡名称
        # self.tab_widget.tabCloseRequested.connect(self.deltab)
        self.tab_widget.tabCloseRequested.connect(self.close_tab)

        # 绘图窗口
        self.paint = PaintRoute()  # 运动轨迹窗口
        self.widget_map = widget_map()
        self.widget_map.layout_route.addWidget(self.paint)
        self.paint.sig_paintRoute.sig_point.connect(self.thread_position.update)
        self.angleCurve = AngleCurve()  # 陀螺仪角度曲线
        self.layout_angleCurve.addWidget(self.angleCurve)

        # 侧边栏
        # self.bar_left.currentRowChanged.connect(lambda:self.fuc_change_stack("设备控制"))
        # self.bar_left.currentRowChanged.connect(self.fuc_change_stack)
        self.bar_left.itemClicked.connect(self.fuc_change_stack)
        self.tab_widget.currentChanged.connect(self.on_tab_changed)
        # self.tab_widget.currentChanged.connect(self.fuc_change_stack)

        # 附属（日志区、视频区）
        self.action_log.toggled.connect(self.toggle_area_log)
        self.action_video.toggled.connect(self.toggle_area_video)

        # 独立窗口
        self.action_watch.toggled.connect(self.toggle_window_debug)
        self.action_mecanum.toggled.connect(self.toggle_widget_mecanum)
        # self.action_servers.toggled.connect(self.toggle_widget_servers)
        self.action_servers.triggered.connect(self.toggle_widget_servers)
        self.action_handle.triggered.connect(self.toggle_widget_handle)
        self.action_map.triggered.connect(self.toggle_widget_map)

        # endregion
        # region # 麦克纳姆轮小车调试
        self.widget_mecanum = widget_mecanum()
        # self.widget_mecanum.show()
        # self.widget_mecanum.S_setPWM_LF.valueChanged.connect(self.fuc_change_wheel_LF)

        self.widget_mecanum.S_setPWM_LF.sliderReleased.connect(self.mecanum_setPWM)
        self.widget_mecanum.S_setPWM_RF.sliderReleased.connect(self.mecanum_setPWM)
        self.widget_mecanum.S_setPWM_LB.sliderReleased.connect(self.mecanum_setPWM)
        self.widget_mecanum.S_setPWM_RB.sliderReleased.connect(self.mecanum_setPWM)
        self.widget_mecanum.S_setSpeed_LF.sliderReleased.connect(self.mecanum_setRPM)
        self.widget_mecanum.S_setSpeed_RF.sliderReleased.connect(self.mecanum_setRPM)
        self.widget_mecanum.S_setSpeed_LB.sliderReleased.connect(self.mecanum_setRPM)
        self.widget_mecanum.S_setSpeed_RB.sliderReleased.connect(self.mecanum_setRPM)
        # self.widget_mecanum.input_setPWM_LB_set.textChanged.connect(self.fuc_change_wheel_pwm1)
        self.mecanum_pwm = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
        self.widget_mecanum.input_setPWM_LF.returnPressed.connect(lambda: self.mecanum_setPWM1('LF'))
        self.widget_mecanum.input_setPWM_RF.returnPressed.connect(lambda: self.mecanum_setPWM1('RF'))
        self.widget_mecanum.input_setPWM_LB.returnPressed.connect(lambda: self.mecanum_setPWM1('LB'))
        self.widget_mecanum.input_setPWM_RB.returnPressed.connect(lambda: self.mecanum_setPWM1('RB'))
        self.widget_mecanum.input_setSpeed_LF.returnPressed.connect(self.mecanum_setRPM1)
        self.widget_mecanum.input_setSpeed_RF.returnPressed.connect(self.mecanum_setRPM1)
        self.widget_mecanum.input_setSpeed_LB.returnPressed.connect(self.mecanum_setRPM1)
        self.widget_mecanum.input_setSpeed_RB.returnPressed.connect(self.mecanum_setRPM1)
        self.widget_mecanum.input_LF_Kp.returnPressed.connect(self.mecanum_pid_set)
        self.widget_mecanum.btn_stop.clicked.connect(self.mecanum_stop)

        self.pid_inputs = [
            self.widget_mecanum.input_LF_Kp,
            self.widget_mecanum.input_LF_Ki,
            self.widget_mecanum.input_LF_Kd,
            self.widget_mecanum.input_RF_Kp,
            self.widget_mecanum.input_RF_Ki,
            self.widget_mecanum.input_RF_Kd,
            self.widget_mecanum.input_LB_Kp,
            self.widget_mecanum.input_LB_Ki,
            self.widget_mecanum.input_LB_Kd,
            self.widget_mecanum.input_RB_Kp,
            self.widget_mecanum.input_RB_Ki,
            self.widget_mecanum.input_RB_Kd,
        ]
        self.widget_mecanum.btn_pid_read.clicked.connect(self.mecanum_pid_read)
        self.widget_mecanum.btn_pid_set.clicked.connect(self.mecanum_pid_set)
        self.widget_mecanum.btn_pid_save.clicked.connect(self.mecanum_pid_save)
        self.widget_mecanum.btn_pid_load.clicked.connect(self.mecanum_pid_load)
        self.widget_mecanum.input_maxSpeed.returnPressed.connect(self.mecanum_setMaxspeed)
        self.mecanum_setMaxspeed()  # 设置速度滑动条的范围
        self.widget_mecanum.check_mode_control.toggled.connect(self.mecanum_mode_control)

        # self.widget_mecanum.S_speedX.sliderReleased.connect(self.mecanum_setSpeed)
        # self.widget_mecanum.input_speedX.returnPressed.connect(self.mecanum_setSpeed1)
        self.widget_mecanum.btn_forward.clicked.connect(self.mecanum_forward)
        self.widget_mecanum.btn_back.clicked.connect(self.mecanum_back)

        self.widget_mecanum.S_speedX.sliderReleased.connect(self.mecanum_setSpeedXYZ)
        self.widget_mecanum.S_speedY.sliderReleased.connect(self.mecanum_setSpeedXYZ)
        self.widget_mecanum.S_speedZ.sliderReleased.connect(self.mecanum_setSpeedXYZ)
        self.widget_mecanum.input_speedX.returnPressed.connect(self.mecanum_setSpeedXYZ1)
        self.widget_mecanum.input_speedY.returnPressed.connect(self.mecanum_setSpeedXYZ1)
        self.widget_mecanum.input_speedZ.returnPressed.connect(self.mecanum_setSpeedXYZ1)
        # endregion
        # region # AGV手柄
        self.flag_calibrate_scale = 0
        self.widget_handle = Window_handle()
        self.widget_handle.btn_calibrate_begin.clicked.connect(self.handle_calibrate_begin)
        self.widget_handle.btn_calibrate_end.clicked.connect(self.handle_calibrate_end)
        # self.custom_rect1 = CustomRectWidget("Horizontal")
        # self.widget_handle.horizontalLayout.addWidget(self.custom_rect1)
        # self.custom_rect2 = CustomRectWidget("Horizontal")
        # self.widget_handle.horizontalLayout_2.addWidget(self.custom_rect2)
        # self.custom_rect3 = CustomRectWidget("Vertical")
        # self.widget_handle.horizontalLayout_3.addStretch(1)
        # self.widget_handle.horizontalLayout_3.addWidget(self.custom_rect3)
        # self.widget_handle.horizontalLayout_3.addStretch(1)
        # self.custom_rect4 = CustomRectWidget("Vertical")
        # self.widget_handle.horizontalLayout_4.addStretch(1)
        # self.widget_handle.horizontalLayout_4.addWidget(self.custom_rect4)
        # self.widget_handle.horizontalLayout_4.addStretch(1)
        # endregion
        # region # 串口配置
        # 初始化serial对象 用于串口通信
        self.ser = serial.Serial()
        # 初始化端口列表
        self.portList = MyComboBox()
        self.widget_connect.gridLayout_com.addWidget(self.portList, 0, 1, 1, 1)  # 添加到几行几列，并占据几行几列的位置
        if self.mode_debug != "串口":
            self.portList.setVisible(False)
        # 打开/关闭串口 按钮
        self.widget_connect.btn_com.clicked.connect(self.fuc_OpenCom)
        self.rxflag = 0
        # endregion
        # region # 服务器控制（作为服务器）
        # self.hostip=extract_ip()
        # print(self.hostip)
        self.hostip = '10.0.0.78'
        self.widget_connect.input_hostip.setText(self.hostip + ':5000')
        self.widget_connect.btn_server.clicked.connect(self.start_server)
        self.flag_server = False
        self.server_thread = ServerThread(self.hostip, 5000)
        # self.server_thread.received_data.connect(self.show_received_data)
        # self.server_thread.server_log.connect(self.fuc_log)
        # self.server_thread.update_clients_list.connect(self.update_clients_list)
        self.widget_log.btn_clear.clicked.connect(self.fuc_clear)
        self.controlDevice = ''
        self.widget_connect.com_clients.currentIndexChanged.connect(self.controlDevice_change)  # 切换控制设备
        self.server_thread.sig_data_process.connect(self.fuc_data_process)  # 接收数据处理
        # endregion
        # region # 连接/断开 服务器 (作为客户端)
        self.widget_connect.check_connect_device.toggled.connect(self.fuc_check_mode_wifiToUart)
        self.widget_connect.com_server.currentIndexChanged.connect(self.fuc_com_server_change)
        # endregion
        # region # 连接多个服务器 (作为客户端)
        self.widget_servers = widget_servers()
        self.servers_checkBoxs = []
        self.servers_inputs = []
        self.servers_labels = []
        self.servers_buttons = []
        self.servers_sliders = []
        self.servers_pgBars = []
        self.servers_thread = []
        # 指定目录和文件名
        directory = parent_dir_path + "/appDoc/list_server/"
        if not os.path.exists(directory):
            os.makedirs(directory)
        filename = 'default.xlsx'
        file_path = os.path.join(directory, filename)
        self.servers_load(file_path)
        self.populate_excel_files(self.widget_servers.comboBox_fileList, directory)
        self.widget_servers.comboBox_fileList.setCurrentText(filename)
        # filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path+"/testDoc/test", "Excel Files (*.xlsx);;All Files (*)")
        # workbook = openpyxl.load_workbook(filename)

        self.widget_servers.pushButton_edit.clicked.connect(self.servers_edit)
        self.widget_servers.pushButton_reload.clicked.connect(self.servers_reload)
        self.widget_servers.btn_connect.clicked.connect(self.servers_connectAll)
        self.widget_servers.btn_disConnect.clicked.connect(self.servers_disConnectAll)
        self.widget_servers.check_all.toggled.connect(self.servers_chooseAll)
        # endregion
        # region # MQTT客户端
        self.w_mqtt = w_mqtt()
        self.w_mqtt.input_ip.setText('10.0.0.246')
        self.w_mqtt.status_connect = False
        self.w_mqtt.btn_server.clicked.connect(self.mqtt_connect_server)
        self.w_mqtt.btn_topic_subscribe.clicked.connect(self.mqtt_subscribe_topic)
        self.w_mqtt.btn_topic_unsubscribe.clicked.connect(self.mqtt_unsubscribe_topic)
        self.w_mqtt.btn_topic_publish.clicked.connect(self.mqtt_publish_topic)

        self.w_mqtt.btn_clearLog.clicked.connect(self.mqtt_clearLog)
        # endregion
        # region # 日志管理
        data_today = datetime.now().strftime('20%y-%m-%d')
        dir_name = 'appDoc/log'
        if not os.path.isdir(dir_name):
            os.makedirs(dir_name)
        self.widget_log.input_record_name.setText(data_today)
        self.widget_log.btn_openlog.clicked.connect(self.fuc_openlog)
        # endregion
        # region # AGV运动控制
        self.btn_agv_stop.clicked.connect(self.fuc_agv_stop)  # 停止
        self.btn_agv_moveForward.clicked.connect(self.fuc_agv_moveForward)  # 前进
        self.btn_agv_moveBack.clicked.connect(self.fuc_agv_moveBack)  # 后退
        self.btn_agv_rotateCW.clicked.connect(self.fuc_agv_rotateCW)  # 正转
        self.btn_agv_rotateCCW.clicked.connect(self.fuc_agv_rotateCCW)  # 反转
        self.btn_agv_run.clicked.connect(self.fuc_agv_run)  # 曲线运动
        self.btn_agv_runBack.clicked.connect(self.fuc_agv_runBack)  # 反向曲线运动
        self.btn_agv_toAngle.clicked.connect(self.fuc_agv_toAngle)  # 旋转到指定角度
        self.btn_agv_toDistance.clicked.connect(self.fuc_agv_toDistance)  # 运行指定距离
        self.btn_agv_toPos.clicked.connect(self.fuc_agv_toPos)  # 运行到指定点
        self.btn_agv_resetPos.clicked.connect(self.fuc_agv_resetPos)  # 位置坐标复位
        self.btn_agv_resetAngle.clicked.connect(self.fuc_agv_resetAngle)  # 角度复位
        self.check_obstacle_ignore.toggled.connect(self.fuc_obstacle_ignore)  # 配置雷达避障
        self.check_watch_agvState.toggled.connect(self.fuc_watch_agvState)  # AGV状态监测开启/关闭
        self.checkBox_enMotor.toggled.connect(self.fuc_enMotor)
        # self.btn_enMotor.clicked.connect(self.fuc_enMotor)
        # self.btn_motor_protect.clicked.connect(self.fuc_motor_protect)
        # endregion
        # region # 转盘及升降机构控制
        self.btn_rotator_CW.clicked.connect(self.fuc_rotator_CW)
        self.btn_rotator_CCW.clicked.connect(self.fuc_rotator_CCW)
        self.btn_rotator_cw.clicked.connect(self.fuc_rotator_CW)
        self.btn_rotator_ccw.clicked.connect(self.fuc_rotator_CCW)
        self.btn_liftor_up.clicked.connect(self.fuc_liftor_up)
        self.btn_liftor_down.clicked.connect(self.fuc_liftor_down)
        self.btn_liftor_down1.clicked.connect(lambda: self.fuc_liftor_upDown(0))
        self.btn_liftor_up1.clicked.connect(lambda: self.fuc_liftor_upDown(1))
        self.btn_stop_RU.clicked.connect(self.fuc_stop_RU)
        self.btn_motorT_mode.clicked.connect(self.fuc_motorT_mode)
        self.btn_motorU_mode.clicked.connect(self.fuc_motorU_mode)
        self.pushButton_findOrigin_U.clicked.connect(lambda: self.fuc_motor_findOrigin(0x04))
        self.pushButton_findOrigin_T.clicked.connect(lambda: self.fuc_motor_findOrigin(0x05))
        self.btn_motorT_setOrigin.clicked.connect(self.fuc_motorT_setOrigin)
        self.btn_motorU_setOrigin.clicked.connect(self.fuc_motorU_setOrigin)
        self.input_motorT_position_T.returnPressed.connect(self.fuc_motorT_position_T)
        self.input_motorU_position_T.returnPressed.connect(self.fuc_motorU_position_T)
        self.input_motorT_angle_T.returnPressed.connect(self.fuc_motorT_angle_T)
        self.input_motorU_height_T.returnPressed.connect(self.fuc_motorU_height_T)
        self.btn_motor_remote_stop.clicked.connect(self.fuc_motor_disconnect)
        self.check_motor_communicate.toggled.connect(self.fuc_motor_communicate)
        self.check_rotator_RR.toggled.connect(self.fuc_rotator_RR)
        self.pushButton_tmp_test.clicked.connect(self.fuc_tmp_test)

        # endregion
        # region # 运动轨迹
        
        dir_name = 'appDoc/trajectory'
        if not os.path.isdir(dir_name):
            os.makedirs(dir_name)
        # self.paint.dotdata=[{'pos': [-676.1, 0.0]},{'pos': [0, 0]}]#初始化轨迹点
        self.paint.scatter.setData(self.paint.dotdata)
        self.dotdata = []
        self.widget_map.btn_clear_points.clicked.connect(self.fuc_clear_points)
        self.widget_map.btn_save_points.clicked.connect(self.fuc_saveTrajectory)
        self.widget_map.btn_load_points.clicked.connect(self.fuc_loadTrajectory)
        self.widget_map.btn_play_points.clicked.connect(lambda: self.fuc_playTrajectory(1))
        self.widget_map.btn_playback_points.clicked.connect(lambda: self.fuc_playTrajectory(0))
        self.widget_map.btn_save_keyPoints.clicked.connect(self.fuc_save_keyDot)
        self.widget_map.btn_load_keyPoints.clicked.connect(self.fuc_load_keyDot)

        self.keyDots = []  # 关键点

        # 地图二维码
        self.widget_map.pushButton_load_map.clicked.connect(self.fuc_load_map_DmCode)

        # 多点路径规划
        self.widget_map.check_pointCycle.clicked.connect(self.fuc_pointCycle)
        self.widget_map.btn_startPointRecycle.clicked.connect(self.fuc_startPointRecycle)
        # endregion
        # region # 轨迹跟踪参数(standly纠偏算法调试)
        self.widget_standly = widget_standly()
        self.widget_standly.btn_trace_query.clicked.connect(self.fuc_trace_query)
        self.widget_standly.btn_trace_set.clicked.connect(self.fuc_trace_set)
        # endregion
        # region # AGV参数配置
        dir_name = 'appDoc/parameter'
        if not os.path.isdir(dir_name):
            os.makedirs(dir_name)
        self.btn_system_sleep.clicked.connect(self.fuc_system_sleep)
        self.btn_system_restart.clicked.connect(self.fuc_system_restart)
        self.btn_system_reset.clicked.connect(self.fuc_system_reset)
        self.btn_software_reset.clicked.connect(self.fuc_software_reset)
        self.btn_read_flashParameter.clicked.connect(self.fuc_read_flashParameter)
        self.btn_set_flashParameter.clicked.connect(self.fuc_set_flashParameter)
        self.btn_save_flashParameter.clicked.connect(self.fuc_save_flashParameter)
        self.btn_save_parameter.clicked.connect(self.fuc_save_parameter)
        self.btn_load_paramete.clicked.connect(self.fuc_load_parameter)
        self.lineEdit_address_nrf24l01.returnPressed.connect(self.fuc_nrf24l01_address)
        self.agv_parameter = [
            'input_software_version',
            'input_scale_rateToLine',
            'input_agv_default_lineSpeed',
            'input_agv_default_rateSpeed',
            'input_agv_max_lineSpeed',
            'input_agv_max_rateSpeed',
            'input_agv_acceleration',
            'input_agv_deceleration',
            'input_agv_RateAcceleration',
            'input_agv_RateDeceleration',
            'input_distanceRangeOffset',
            'input_angleRangeOffset',
            'input_LW_compensation',
            'input_RW_compensation',
            'input_LW_offset',
            'input_RW_offset',
            'input_factor_lineSpeed',
            'input_factor_rotateSpeed',
            'input_factor_rectify',
            'input_rectifyAngle',
            'input_imu_integralGain',
            'input_imu_offsetX',
            'input_imu_offsetY',
            'input_imu_offsetZ',
        ]
        self.angle_rectify_last = None
        # endregion
        # region # AGV程序升级
        self.widget_OTA = widget_OTA()
        self.widget_OTA.gridLayout_5.addWidget(self.widget_servers, 0, 0, 1, 1)
        self.widget_OTA.btn_iap_in.clicked.connect(self.fuc_iap_in)  # 进入bootloader
        self.widget_OTA.btn_app_in.clicked.connect(self.fuc_app_in)  # 进入APP（取消升级）
        self.widget_OTA.btn_iap_sendLen.clicked.connect(self.fuc_iap_sendLen)  # 发送固件长度
        self.widget_OTA.btn_receive_in.clicked.connect(self.fuc_receive_in)  # 进入接收固件状态
        self.widget_OTA.btn_iap_sendBin.clicked.connect(self.fuc_iap_sendBin)  # 8266\串口升级
        self.widget_OTA.btn_updateProgram.clicked.connect(self.fuc_updateProgram)  # 一键升级
        self.iap_filename = ''
        self.packLen = 512

        if self.widget_OTA.input_app_address.text() == '':
            self.widget_OTA.input_app_address.setText(self.app_address)
        # endregion
        # region # 校准IMU（陀螺仪）
        self.flag_imu_test = 0
        self.btn_imu_testCW.clicked.connect(lambda: self.fuc_imu_test(1))
        self.btn_imu_testCCW.clicked.connect(lambda: self.fuc_imu_test(0))
        self.btn_imu_test.clicked.connect(lambda: self.fuc_imu_test(2))

        # 标签页
        self.widget_imu = widget_imu()
        self.widget_imu.slider_rateSpeed.valueChanged.connect(self.fuc_set_rateSpeed)
        self.widget_imu.btn_rateSpeed.clicked.connect(self.fuc_reset_rateSpeed)
        self.widget_imu.input_rateSpeed.returnPressed.connect(self.fuc_set1_rateSpeed)
        self.widget_imu.btn_reset_curve.clicked.connect(self.fuc_reset_curve)
        self.widget_imu.btn_offset_calibration.clicked.connect(self.fuc_offset_calibration)
        # self.tab_imu = QWidget()
        # layout = QVBoxLayout()
        # layout.addWidget(self.widget_imu)
        # self.tab_imu.setLayout(layout)
        # endregion
        # region # 二维码标定
        # 创建标签页
        self.page_codeNum = 50  # 每页显示的二维码个数
        self.widget_tags = widget_tags()

        # self.tab_widget.addTab(self.tab_tags, '二维码标定')
        # self.btn_query_DmCode.clicked.connect(self.fuc_query_DmCode)
        # self.btn_set_DmCode.clicked.connect(self.fuc_set_DmCode)
        # self.btn_DmCode_next.clicked.connect(self.fuc_page_next)
        # self.btn_DmCode_pre.clicked.connect(self.fuc_page_pre)
        # self.btn_save_DmCode.clicked.connect(self.fuc_save_DmCode)
        # self.btn_load_DmCode.clicked.connect(self.fuc_load_DmCode)
        # self.btn_clear_DmCode.clicked.connect(self.fuc_clear_Dmcode)
        self.widget_tags.btn_tags_query.clicked.connect(self.fuc_query_DmCode)
        self.widget_tags.btn_tags_set.clicked.connect(self.fuc_set_DmCode)
        self.widget_tags.btn_tags_save.clicked.connect(self.fuc_save_DmCode)
        self.widget_tags.btn_tags_load.clicked.connect(self.fuc_load_DmCode)
        self.widget_tags.btn_page_pre.clicked.connect(self.fuc_page_pre)
        self.widget_tags.btn_page_next.clicked.connect(self.fuc_page_next)
        self.widget_tags.input_page_current.returnPressed.connect(self.fuc_page_jump)
        self.widget_tags.check_all.toggled.connect(self.fuc_check_DmCode)
        # endregion
        # region # 语音播报
        self.widget_audio = widget_audio()
        self.flag_voice_download = 0
        self.widget_audio.btn_voice_play.clicked.connect(self.fuc_voice_play)
        self.widget_audio.slider_volume.sliderReleased.connect(self.fuc_voice_setVolume)

        self.widget_audio.btn_voice_addFile.clicked.connect(self.fuc_voice_addFile)
        self.widget_audio.btn_voice_addFiles.clicked.connect(self.fuc_voice_addFiles)
        self.widget_audio.btn_voice_moveUp.clicked.connect(self.fuc_voice_moveUp)
        self.widget_audio.btn_voice_moveDown.clicked.connect(self.fuc_voice_moveDown)
        self.widget_audio.btn_voice_delFile.clicked.connect(self.fuc_voice_delFile)
        self.widget_audio.btn_voice_delFiles.clicked.connect(self.fuc_voice_delFiles)
        self.widget_audio.btn_voice_downloadSel.clicked.connect(self.fuc_voice_downloadSel)
        self.widget_audio.btn_voice_downloadAll.clicked.connect(self.fuc_voice_downloadAll)
        self.widget_audio.btn_voice_update.clicked.connect(self.fuc_voice_update)
        self.widget_audio.btn_voice_exit.clicked.connect(self.fuc_voice_exit)

        # endregion
        # region # WIFI参数设置
        self.widget_wifi = widget_wifi()
        # endregion
        # region # AGV
        self.layout_log.addWidget(self.widget_log)
        self.layout_connect.addWidget(self.widget_connect)
        # endregion
        # region # 输入输出
        self.widget_IO = widget_IO()
        self.led_inputors = []
        i = 0
        vLayout = QVBoxLayout()
        for i in range(4):
            hLayout = QHBoxLayout()
            for j in range(7):
                button_test = QPushButton()
                self.led_inputor = Inputor(button_test)
                self.led_inputors.append(self.led_inputor)
                label = QLabel(f'输入 {i*7 + j + 1}')
                hLayout.addWidget(button_test)
                hLayout.addWidget(label)
            vLayout.addLayout(hLayout)
        self.widget_IO.groupBox_7.setLayout(vLayout)

        self.widget_IO.check_out_1.toggled.connect(lambda: self.fuc_outputGroupControl(1))
        self.widget_IO.check_out_2.toggled.connect(lambda: self.fuc_outputGroupControl(2))
        self.widget_IO.check_out_3.toggled.connect(lambda: self.fuc_outputGroupControl(3))
        self.widget_IO.check_out_4.toggled.connect(lambda: self.fuc_outputGroupControl(4))
        self.widget_IO.check_out_5.toggled.connect(lambda: self.fuc_outputGroupControl(5))
        self.widget_IO.check_out_6.toggled.connect(lambda: self.fuc_outputGroupControl(6))
        self.widget_IO.check_out_7.toggled.connect(lambda: self.fuc_outputGroupControl(7))
        self.widget_IO.check_out_8.toggled.connect(lambda: self.fuc_outputGroupControl(8))
        self.widget_IO.check_out_9.toggled.connect(lambda: self.fuc_outputGroupControl(9))
        self.widget_IO.check_out_10.toggled.connect(lambda: self.fuc_outputGroupControl(10))
        self.widget_IO.check_out_11.toggled.connect(lambda: self.fuc_outputGroupControl(11))
        self.widget_IO.check_out_12.toggled.connect(lambda: self.fuc_outputGroupControl(12))
        self.widget_IO.check_out_13.toggled.connect(lambda: self.fuc_outputGroupControl(13))
        self.widget_IO.check_out_14.toggled.connect(lambda: self.fuc_outputGroupControl(14))
        self.widget_IO.check_out_15.toggled.connect(lambda: self.fuc_outputGroupControl(15))
        self.widget_IO.check_out_16.toggled.connect(lambda: self.fuc_outputGroupControl(16))
        self.widget_IO.check_out_17.toggled.connect(lambda: self.fuc_outputGroupControl(17))
        self.widget_IO.check_out_18.toggled.connect(lambda: self.fuc_outputGroupControl(18))
        self.widget_IO.check_out_19.toggled.connect(lambda: self.fuc_outputGroupControl(19))
        self.widget_IO.check_out_20.toggled.connect(lambda: self.fuc_outputGroupControl(20))
        self.widget_IO.check_out_21.toggled.connect(lambda: self.fuc_outputGroupControl(21))
        self.widget_IO.check_out_22.toggled.connect(lambda: self.fuc_outputGroupControl(22))
        self.widget_IO.check_out_23.toggled.connect(lambda: self.fuc_outputGroupControl(23))
        self.widget_IO.check_out_24.toggled.connect(lambda: self.fuc_outputGroupControl(24))

        self.widget_IO.lineEdit_outputs_control.returnPressed.connect(self.fuc_outputs_control)
        self.widget_IO.lineEdit_led_control.returnPressed.connect(self.fuc_led_control)
        self.widget_IO.lineEdit_interface_heartbeat.returnPressed.connect(self.fuc_interface_heartbeat)
        self.widget_IO.lineEdit_avoid_area.returnPressed.connect(self.fuc_change_avoid_area)
        # endregion
        # region # 控制板测试
        self.widget_testBoard = widget_testBoard()
        self.widget_testBoard.IO = []
        vLayout = QVBoxLayout()
        for i in range(3):
            hLayout = QHBoxLayout()
            for j in range(8):
                checkbox = QCheckBox(f'IO {i*8 + j + 1}')
                self.widget_testBoard.IO.append(checkbox)
                hLayout.addWidget(checkbox)
            vLayout.addLayout(hLayout)
        self.widget_testBoard.groupBox.setLayout(vLayout)

        self.widget_testBoard.communicate = []
        self.widget_testBoard.comText = ['转接板', '2.4G', '4851发4852收',
                                         '4852发4851收', '4853发4854收', '4854发4853收', 'CAN1发CAN2收', 'CAN2发CAN1收']
        vLayout = QVBoxLayout()
        for i in range(4):
            hLayout = QHBoxLayout()
            for j in range(2):
                checkbox = QCheckBox(self.widget_testBoard.comText[i*2 + j])
                self.widget_testBoard.communicate.append(checkbox)
                hLayout.addWidget(checkbox)
            vLayout.addLayout(hLayout)
        self.widget_testBoard.groupBox_2.setLayout(vLayout)

        self.widget_testBoard.pushButton_test.clicked.connect(self.testBoard_test)
        self.widget_testBoard.pushButton_retest.clicked.connect(self.testBoard_retest)
        self.widget_testBoard.pushButton_testSelect.clicked.connect(self.testBoard_testSelect)
        # endregion
        # region # 清洗机
        self.widget_clean = widget_clean()
        # self.widget_clean.layout_connect.addWidget(self.widget_connect)
        # self.widget_clean.layout_log.addWidget(self.widget_log)
        self.widget_clean.checkBox_scrub.toggled.connect(self.fuc_scrub)
        self.widget_clean.checkBox_water_valve.toggled.connect(self.fuc_water_valve)
        self.widget_clean.checkBox_sewage_valve.toggled.connect(self.fuc_sewage_valve)
        self.widget_clean.checkBox_vacuum.toggled.connect(self.clean_vacuum_power)

        self.widget_clean.pushButton_scrub_stop.clicked.connect(
            lambda: self.fuc_pushRod(0x01, 0x00))  # 刷盘01，刮耙02， 00：停止 01：上升  02：下降
        self.widget_clean.pushButton_scrub_up.clicked.connect(lambda: self.fuc_pushRod(0x01, 0x01))
        self.widget_clean.pushButton_scrub_down.clicked.connect(lambda: self.fuc_pushRod(0x01, 0x02))
        self.widget_clean.pushButton_scraper_stop.clicked.connect(lambda: self.fuc_pushRod(0x02, 0x00))
        self.widget_clean.pushButton_scraper_up.clicked.connect(lambda: self.fuc_pushRod(0x02, 0x01))
        self.widget_clean.pushButton_scraper_down.clicked.connect(lambda: self.fuc_pushRod(0x02, 0x02))

        self.widget_clean.horizontalSlider_vacuum.sliderReleased.connect(self.fuc_vacuum)
        self.widget_clean.horizontalSlider_water_pump.sliderReleased.connect(self.fuc_water_pump)
        self.widget_clean.lineEdit_vacuum.returnPressed.connect(self.fuc_vacuum_input)
        self.widget_clean.lineEdit_water_pump.returnPressed.connect(self.fuc_water_pump_input)
        self.widget_clean.pushButton_clean_stop.clicked.connect(lambda: self.fuc_clean_move(0x00))
        self.widget_clean.pushButton_clean_forward.clicked.connect(lambda: self.fuc_clean_move(0x01))
        self.widget_clean.pushButton_clean_backward.clicked.connect(lambda: self.fuc_clean_move(0x02))
        self.widget_clean.pushButton_clean_turnLeft.clicked.connect(lambda: self.fuc_clean_move(0x03))
        self.widget_clean.pushButton_clean_turnRight.clicked.connect(lambda: self.fuc_clean_move(0x04))

        self.widget_clean.lineEdit_led_control.returnPressed.connect(self.clean_led_control)
        self.widget_clean.lineEdit_voice_control.returnPressed.connect(self.clean_voice_control)
        self.widget_clean.lineEdit_avoid_area.returnPressed.connect(self.clean_change_avoid_area)
        # endregion
        # region # 清洗机驱动板测试
        self.widget_clean_driver = widget_clean_driver()
        self.widget_clean_driver.slider_driver_pwm.sliderReleased.connect(self.fuc_driver_pwm)
        self.widget_clean_driver.slider_driver_speed.sliderReleased.connect(self.fuc_driver_speed)
        # endregion
        # region # 清洗机工作站
        self.widget_clean.checkBox_station_led_charge.toggled.connect(lambda: self.clean_output_control(0x01))
        self.widget_clean.checkBox_station_led_water.toggled.connect(lambda: self.clean_output_control(0x02))
        self.widget_clean.checkBox_station_led_water2.toggled.connect(lambda: self.clean_output_control(0x03))
        self.widget_clean.checkBox_station_led_clean.toggled.connect(lambda: self.clean_output_control(0x04))
        self.widget_clean.checkBox_station_led_green.toggled.connect(lambda: self.clean_output_control(0x05))
        self.widget_clean.checkBox_station_led_red.toggled.connect(lambda: self.clean_output_control(0x06))
        self.widget_clean.checkBox_station_led_yellow.toggled.connect(lambda: self.clean_output_control(0x07))
        self.widget_clean.checkBox_station_out_extend.toggled.connect(lambda: self.clean_output_control(0x08))
        self.widget_clean.checkBox_station_out_retract.toggled.connect(lambda: self.clean_output_control(0x09))
        self.widget_clean.checkBox_station_valve_clean.toggled.connect(lambda: self.clean_output_control(0x0A))
        self.widget_clean.checkBox_station_valve_water.toggled.connect(lambda: self.clean_output_control(0x0B))
        self.widget_clean.checkBox_station_charge_2.toggled.connect(self.cleanStation_charge)
        # endregion
        # region # CTU
        self.widget_ctu_up = widget_ctu_up()
        # 设置默认位置
        file_path = parent_dir_path + "/appDoc/config/location.txt"
        locations = []
        if os.path.exists(file_path):
            with open(file_path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
            if lines == []:
                print('文件无内容')
            else:
                for line in lines:
                    if '#' in line:
                        line = line[:line.index('#')]  # 去除每行#后的内容
                    if line.strip():  # 去除字符串两端的空白字符（包括空格、换行符和制表符）
                        location = line.strip().split(":")[1]
                        locations.append(location)
        else:
            print('文件不存在')
        # print(locations)
        self.widget_ctu_up.input_motor2_position_T.setText(locations[0])
        self.widget_ctu_up.input_motor3_position_T.setText(locations[1])
        self.widget_ctu_up.input_motor4_position_T.setText(locations[2])
        self.widget_ctu_up.input_motor5_position_T.setText(locations[3])

        # 使能控制
        self.widget_ctu_up.checkBox_en_motor2.toggled.connect(lambda: self.ctu_motor_en(0x02))
        self.widget_ctu_up.checkBox_en_motor3.toggled.connect(lambda: self.ctu_motor_en(0x03))
        self.widget_ctu_up.checkBox_en_motor4.toggled.connect(lambda: self.ctu_motor_en(0x04))
        self.widget_ctu_up.checkBox_en_motor5.toggled.connect(lambda: self.ctu_motor_en(0x05))
        # 模式设置
        self.widget_ctu_up.btn_motor2_mode.clicked.connect(lambda: self.ctu_motor_mode(0x02))
        self.widget_ctu_up.btn_motor3_mode.clicked.connect(lambda: self.ctu_motor_mode(0x03))
        self.widget_ctu_up.btn_motor4_mode.clicked.connect(lambda: self.ctu_motor_mode(0x04))
        self.widget_ctu_up.btn_motor5_mode.clicked.connect(lambda: self.ctu_motor_mode(0x05))
        # 原点标定
        self.widget_ctu_up.btn_motor2_setOrigin.clicked.connect(lambda: self.ctu_motor_setOrigin(0x02))
        self.widget_ctu_up.btn_motor3_setOrigin.clicked.connect(lambda: self.ctu_motor_setOrigin(0x03))
        self.widget_ctu_up.btn_motor4_setOrigin.clicked.connect(lambda: self.ctu_motor_setOrigin(0x04))
        self.widget_ctu_up.btn_motor5_setOrigin.clicked.connect(lambda: self.ctu_motor_setOrigin(0x05))
        # 电机运行到指定位置
        self.widget_ctu_up.input_motor2_position_T.returnPressed.connect(lambda: self.ctu_motor_position_T(0x02))
        self.widget_ctu_up.input_motor3_position_T.returnPressed.connect(lambda: self.ctu_motor_position_T(0x03))
        self.widget_ctu_up.input_motor4_position_T.returnPressed.connect(lambda: self.ctu_motor_position_T(0x04))
        self.widget_ctu_up.input_motor5_position_T.returnPressed.connect(lambda: self.ctu_motor_position_T(0x05))
        # 机构运行到指定位置
        self.widget_ctu_up.input_motor2_location_T.returnPressed.connect(lambda: self.ctu_device_location_T(0x02))
        self.widget_ctu_up.input_motor3_location_T.returnPressed.connect(lambda: self.ctu_device_location_T(0x03))
        self.widget_ctu_up.input_motor4_location_T.returnPressed.connect(lambda: self.ctu_device_location_T(0x04))
        self.widget_ctu_up.input_motor5_location_T.returnPressed.connect(lambda: self.ctu_device_location_T(0x05))
        # 电机以指定速度运行
        self.widget_ctu_up.btn_motor2_speed0.clicked.connect(lambda: self.ctu_motor_control(0x02, 0))
        self.widget_ctu_up.btn_motor2_speed1.clicked.connect(lambda: self.ctu_motor_control(0x02, 1))
        self.widget_ctu_up.btn_motor2_speed2.clicked.connect(lambda: self.ctu_motor_control(0x02, 2))
        self.widget_ctu_up.btn_motor3_speed0.clicked.connect(lambda: self.ctu_motor_control(0x03, 0))
        self.widget_ctu_up.btn_motor3_speed1.clicked.connect(lambda: self.ctu_motor_control(0x03, 1))
        self.widget_ctu_up.btn_motor3_speed2.clicked.connect(lambda: self.ctu_motor_control(0x03, 2))
        self.widget_ctu_up.btn_motor4_speed0.clicked.connect(lambda: self.ctu_motor_control(0x04, 0))
        self.widget_ctu_up.btn_motor4_speed1.clicked.connect(lambda: self.ctu_motor_control(0x04, 1))
        self.widget_ctu_up.btn_motor4_speed2.clicked.connect(lambda: self.ctu_motor_control(0x04, 2))
        self.widget_ctu_up.btn_motor5_speed0.clicked.connect(lambda: self.ctu_motor_control(0x05, 0))
        self.widget_ctu_up.btn_motor5_speed1.clicked.connect(lambda: self.ctu_motor_control(0x05, 1))
        self.widget_ctu_up.btn_motor5_speed2.clicked.connect(lambda: self.ctu_motor_control(0x05, 2))
        # 拨杆电机控制
        self.widget_ctu_up.checkBox_paddle.toggled.connect(lambda: self.ctu_device_control(0x06))
        # self.widget_ctu_up.pushButton_motor6_open.clicked.connect(lambda: self.ctu_device_control(0x06, 1))
        # self.widget_ctu_up.pushButton_motor6_close.clicked.connect(lambda: self.ctu_device_control(0x06, 0))
        # 输出控制
        self.widget_ctu_up.checkBox_out_liftST.toggled.connect(lambda: self.ctu_output_control(0x01))
        self.widget_ctu_up.checkBox_out_rotateST.toggled.connect(lambda: self.ctu_output_control(0x02))
        self.widget_ctu_up.checkBox_out_liftPWR.toggled.connect(lambda: self.ctu_output_control(0x01))
        self.widget_ctu_up.checkBox_out_walkPWR.toggled.connect(lambda: self.ctu_output_control(0x02))
        self.widget_ctu_up.checkBox_out_radarF1.toggled.connect(lambda: self.ctu_output_control(0x03))
        self.widget_ctu_up.checkBox_out_radarF2.toggled.connect(lambda: self.ctu_output_control(0x04))
        self.widget_ctu_up.checkBox_out_radarF3.toggled.connect(lambda: self.ctu_output_control(0x05))
        self.widget_ctu_up.checkBox_out_radarF4.toggled.connect(lambda: self.ctu_output_control(0x06))
        self.widget_ctu_up.checkBox_out_radarB1.toggled.connect(lambda: self.ctu_output_control(0x07))
        self.widget_ctu_up.checkBox_out_radarB2.toggled.connect(lambda: self.ctu_output_control(0x08))
        self.widget_ctu_up.checkBox_out_radarB3.toggled.connect(lambda: self.ctu_output_control(0x09))
        self.widget_ctu_up.checkBox_out_radarB4.toggled.connect(lambda: self.ctu_output_control(0x0A))
        self.widget_ctu_up.checkBox_out_sto.toggled.connect(lambda: self.ctu_output_control(0x0B))
        self.widget_ctu_up.checkBox_out_ledLR.toggled.connect(lambda: self.ctu_output_control(0x0D))
        self.widget_ctu_up.checkBox_out_ledLG.toggled.connect(lambda: self.ctu_output_control(0x0E))
        self.widget_ctu_up.checkBox_out_ledLY.toggled.connect(lambda: self.ctu_output_control(0x0F))
        self.widget_ctu_up.checkBox_out_ledRR.toggled.connect(lambda: self.ctu_output_control(0x10))
        self.widget_ctu_up.checkBox_out_ledRG.toggled.connect(lambda: self.ctu_output_control(0x11))
        self.widget_ctu_up.checkBox_out_ledRY.toggled.connect(lambda: self.ctu_output_control(0x12))
        # 组合控制
        self.widget_ctu_up.pushButton_excecute_goods.clicked.connect(self.ctu_excecute_goods)
        self.widget_ctu_up.pushButton_emergencyStop.clicked.connect(self.ctu_emergencyStop)
        self.widget_ctu_up.pushButton_reset.clicked.connect(self.ctu_reset)
        self.widget_ctu_up.checkBox_force.toggled.connect(self.ctu_force)
        self.widget_ctu_up.checkBox_manual_auto.toggled.connect(self.ctu_manual_auto)
        # endregion
        # region # LED
        self.widget_leds = widget_leds()
        self.widget_leds.lineEdit_led_num.returnPressed.connect(self.led_num)
        self.widget_leds.comboBox_led_mode.clear()
        self.widget_leds.comboBox_led_mode.addItems(['常亮', '闪烁', '流水', '呼吸', '渐变'])
        self.widget_leds.comboBox_led_mode.currentIndexChanged.connect(self.led_mode)
        self.widget_leds.comboBox_led_color.clear()
        self.widget_leds.comboBox_led_color.addItems(['灭', '红', '橙', '黄', '绿', '蓝', '靛', '紫', '白'])
        self.widget_leds.comboBox_led_color.currentIndexChanged.connect(self.led_color)
        self.widget_leds.horizontalSlider_led_light.sliderReleased.connect(self.led_light)
        self.widget_leds.lineEdit_led_light.returnPressed.connect(self.led_light_input)
        self.widget_leds.horizontalSlider_led_speed.sliderReleased.connect(self.led_speed)
        self.widget_leds.lineEdit_led_speed.returnPressed.connect(self.led_speed_input)
        # endregion
        # region # 串口设置
        # endregion
        # region # 调度模拟
        self.S_cmd_default = []
        self.cmdNum = 0  # 指令序号0-9999
        self.count_cmdDone = 0
        check = QCheckBox()
        check.toggled.connect(self.fuc_check_cmd_all)
        self.layout_cmd.addWidget(check, 0, 0, 1, 1)
        # ['指令码','指令名','P1','P2','P3','P4','P5','P6','P7','状态']
        textlist = ['指令码', '指令名', 'P1', 'P2', 'P3', 'P4', 'P5', 'P6', 'P7']
        self.cmd_column = len(textlist) + 1
        for i in range(len(textlist)):  # 创建第一行标签
            label = QLabel(textlist[i])
            # label.setObjectName(f'label_{i+1}')
            self.layout_cmd.addWidget(label, 0, i + 1, 1, 1)
        num_cmd = 10
        self.num_cmd = 10
        for i in range(1):  # 创建复选框  #i为所在列，j为0-10行
            for j in range(num_cmd):
                check = QCheckBox()
                self.layout_cmd.addWidget(check, j + 1, 0, 1, 1)
        for i in range(1):  # 创建输入框
            for j in range(num_cmd):
                line_edit = QLineEdit()
                self.layout_cmd.addWidget(line_edit, j + 1, i + 1, 1, 1)
        for i in range(1):  # 创建下拉框
            for j in range(num_cmd):
                com = QComboBox()
                self.layout_cmd.addWidget(com, j + 1, i + 2, 1, 1)
        for i in range(7):  # 创建输入框
            for j in range(num_cmd):
                line_edit = QLineEdit()
                self.layout_cmd.addWidget(line_edit, j + 1, i + 3, 1, 1)
        # for i in range(1):#创建标签
        #     for j in range(num_cmd):
        #         label = QLabel('未开始')
        #         self.layout_cmd.addWidget(label, j+1, i+10, 1, 1)
        cmdDic = {'01': 'ST', '02': 'GP', "03": 'SP', "04": 'RV', "05": 'RA', "06": 'HL', "07": 'SL', "08": 'CF'}
        # for i in range(10):#指令码赋初值
        #     widget = self.layout_cmd.itemAt(11+(1)*num_cmd+i).widget()
        #     widget.clear()
        #     widget.addItems(list(cmdDic.keys()))
        # widget.currentIndexChanged.connect(self.fuc_cmdNumChange)
        for i in range(10):  # 指令名赋初值
            widget = self.layout_cmd.itemAt(self.cmd_column + (2) * num_cmd + i).widget()
            widget.clear()
            widget.addItems(list(cmdDic.values()))

            # widget.currentIndexChanged.connect(self.fuc_cmdNameChange)
        for i in range(10):  # 复选框勾选时添加命令号
            widget = self.layout_cmd.itemAt(self.cmd_column + (0) * num_cmd + i).widget()
            widget.stateChanged.connect(self.fuc_change_cmdNum)
        self.btn_cmd_save.clicked.connect(self.fuc_cmdSave)
        self.btn_cmd_load.clicked.connect(self.fuc_cmdLoad)
        self.btn_sendCmd.clicked.connect(self.fuc_sendCmd_TCS)
        self.check_switchAuthority.toggled.connect(self.fuc_switchAuthority)
        # widget = self.layout_cmd.itemAt(11+3*num_cmd+0).widget()#第一行有十一个参数，每列有10个命令
        # widget.setText('已开始')
        # widget = self.layout_cmd.itemAt(11+(3+6)*num_cmd+9).widget()
        # widget.setText('已开始')
        # for i in range(self.layout_cmd.count()):#打印对象名称
        # widget = self.layout_cmd.itemAt(i).widget()
        # print(widget.objectName())

        # endregion
        # region # 调度监测
        self.widget_tcs_watch = widget_tcs_watch()
        self.thread_playData = None
        self.tcs_datas = None
        dir_name = 'appDoc/record_data'
        if not os.path.isdir(dir_name):
            os.makedirs(dir_name)
        self.widget_tcs_watch.pushButton_load_data.clicked.connect(self.tcs_load_data)
        self.widget_tcs_watch.pushButton_play_data.clicked.connect(self.tcs_play_data)
        self.widget_tcs_watch.pushButton_play_pause.clicked.connect(self.tcs_play_pause)
        self.widget_tcs_watch.pushButton_play_continue.clicked.connect(self.tcs_play_continue)
        self.widget_tcs_watch.lineEdit_play_multiSpeed.returnPressed.connect(self.tcs_play_multiSpeed)
        self.widget_tcs_watch.lineEdit_play_current.returnPressed.connect(self.tcs_jump_progress)
        # endregion
        # region # 调度日志分析
        self.widget_log_analyse = widget_log_analyse()
        self.filepath_log_analyse = ''
        self.filtered_logs = []
        self.widget_log_analyse.pushButton_select_log.clicked.connect(self.log_analyse_select_log)
        self.widget_log_analyse.pushButton_start.clicked.connect(self.log_analyse_start)
        self.widget_log_analyse.pushButton_addToMap.clicked.connect(self.log_analyse_addToMap)
        # endregion
        # region # 参数配置_上位机2
        tabname = self.widget_config.p_tabwidget_id.currentText()
        if tabname == '远程升级':
            self.widget_OTA.layout_connect.addWidget(self.widget_connect)
            self.widget_OTA.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_OTA, tabname)
        elif tabname == '二维码标定':
            self.tab_widget.addTab(self.widget_tags, tabname)
        elif tabname == '陀螺仪数据':
            self.tab_widget.addTab(self.widget_imu, tabname)
        elif tabname == 'standly纠偏':
            self.tab_widget.addTab(self.widget_standly, tabname)
        elif tabname == 'WIFI参数':
            self.tab_widget.addTab(self.widget_wifi, tabname)
        elif tabname == 'license授权':
            self.tab_widget.addTab(self.widget_license, tabname)
        elif tabname == '控制板测试':
            self.widget_testBoard.layout_connect.addWidget(self.widget_connect)
            self.widget_testBoard.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_testBoard, tabname)
        elif tabname == '清洗机':
            self.widget_clean.layout_connect.addWidget(self.widget_connect)
            self.widget_clean.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_clean, tabname)
        elif tabname == '清洗机驱动板':
            self.widget_clean_driver.layout_connect.addWidget(self.widget_connect)
            self.widget_clean_driver.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_clean_driver, tabname)
        elif tabname == "CTU":
            self.widget_ctu_up.layout_connect.addWidget(self.widget_connect)
            self.widget_ctu_up.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_ctu_up, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "LED":
            self.widget_leds.layout_connect.addWidget(self.widget_connect)
            self.widget_leds.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_leds, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == '调度监测':
            self.widget_tcs_watch.layout_connect.addWidget(self.widget_connect)
            self.widget_tcs_watch.layout_log.addWidget(self.widget_log)
            self.tab_widget.addTab(self.widget_tcs_watch, tabname)
        elif tabname == '日志分析':
            self.tab_widget.addTab(self.widget_log_analyse, tabname)

        p_tabwidget_id = self.get_index_fromTabname(tabname)
        self.tab_widget.setCurrentIndex(p_tabwidget_id)  # 显示标签页
        # endregion
        # region # AGV内部参数监测界面
        self.window_debug = Window_debug()
        self.window_debug.dataColor = []  # 默认颜色

        self.window_debug.plot_widget = pg.GraphicsLayoutWidget()  # 详细参数监测曲线
        self.window_debug.plot_widget.setMinimumHeight(500)
        self.window_debug.layout_plot.addWidget(self.window_debug.plot_widget, 0, 0)
        self.window_debug.plot_item1 = self.window_debug.plot_widget.addPlot()
        self.window_debug.plot_item1.addLegend(size=(150, 80))
        self.window_debug.plot_item1.showGrid(x=True, y=True, alpha=0.5)  # 网格透明度
        self.window_debug.btn_config.clicked.connect(self.show_config)
        # self.window_debug.btn_hideMain.clicked.connect(self.hide_main)
        self.window_debug.check_agv_x.toggled.connect(lambda state: self.showGraph(state, 'agv_x'))
        self.window_debug.check_agv_y.toggled.connect(lambda state: self.showGraph(state, 'agv_y'))
        self.window_debug.check_agv_angle.toggled.connect(lambda state: self.showGraph(state, 'agv_angle'))
        self.window_debug.check_agv_battery.toggled.connect(lambda state: self.showGraph(state, 'agv_battery'))
        # self.window_debug.check_agv_speed_tl.toggled.connect(lambda state:self.showGraph(state,'agv_speed_tl'))#t:target调度下发目标 l:line线速度
        # self.window_debug.check_agv_speed_ac.toggled.connect(lambda state:self.showGraph(state,'agv_speed_ac'))#ac:accelerate 加速度
        # self.window_debug.check_agv_location_up.toggled.connect(lambda state:self.showGraph(state,'agv_location_up'))
        self.window_debug.check_imu_angle.toggled.connect(lambda state: self.showGraph(state, 'imu_angle'))
        self.window_debug.check_agv_speed_pl.toggled.connect(
            lambda state: self.showGraph(state, 'agv_speed_pl'))  # p:plan规划 l:line线速度
        self.window_debug.check_agv_speed_al.toggled.connect(lambda state: self.showGraph(state, 'agv_speed_al'))
        self.window_debug.check_agv_speed_pa.toggled.connect(lambda state: self.showGraph(state, 'agv_speed_pa'))
        self.window_debug.check_agv_speed_aa.toggled.connect(
            lambda state: self.showGraph(state, 'agv_speed_aa'))  # a:actual实际 a:angle角速度
        self.window_debug.check_motorL_speed_p.toggled.connect(lambda state: self.showGraph(state, 'motorL_speed_p'))
        self.window_debug.check_motorL_speed_a.toggled.connect(lambda state: self.showGraph(state, 'motorL_speed_a'))
        self.window_debug.check_motorR_speed_p.toggled.connect(lambda state: self.showGraph(state, 'motorR_speed_p'))
        self.window_debug.check_motorR_speed_a.toggled.connect(lambda state: self.showGraph(state, 'motorR_speed_a'))
        self.window_debug.check_motorU_speed_p.toggled.connect(lambda state: self.showGraph(state, 'motorU_speed_p'))
        self.window_debug.check_motorU_speed_a.toggled.connect(lambda state: self.showGraph(state, 'motorU_speed_a'))
        self.window_debug.check_motorT_speed_p.toggled.connect(lambda state: self.showGraph(state, 'motorT_speed_p'))
        self.window_debug.check_motorT_speed_a.toggled.connect(lambda state: self.showGraph(state, 'motorT_speed_a'))

        self.window_debug.sig_close.connect(self.close_watch)

        # 显示曲线配置
        self.widget_graphConfig = widget_graphConfig()
        layout = self.widget_graphConfig.gridLayout
        self.group = []
        file_path = parent_dir_path + "/appDoc/config/array_color.txt"
        try:
            with open(file_path, 'r') as file:
                lines = file.readlines()
                for i in range(0, 20):
                    if i < len(lines):
                        self.window_debug.dataColor.append(lines[i].strip())
                    else:
                        self.window_debug.dataColor.append('red')
                        print('hlly')
            # print(self.window_debug.dataColor)
            for i in range(0, 20):
                layout1 = QHBoxLayout()
                group1 = QButtonGroup()
                self.group.append(group1)
                color = ['r', 'g', 'b', 'c', 'm', 'y', 'orange']
                color_name = ['red', 'green', 'blue', 'cyan', 'magenta', 'yellow', 'orange']
                for index, item in enumerate(color):
                    radio = QRadioButton(item)
                    radio.setObjectName(str(i))
                    radio.color = color_name[index]
                    radio.clicked.connect(self.on_radioButton_toggled)
                    self.group[i].addButton(radio)
                    layout1.addWidget(radio)
                button = QPushButton()
                button.setObjectName(str(i))
                button.setStyleSheet('QWidget {background-color:%s}' % self.window_debug.dataColor[i])
                button.clicked.connect(self.showColorDialog)
                self.group[i].addButton(button)
                layout1.addWidget(button)
                layout.addLayout(layout1, i, 1)
        except:
            print('曲线颜色配置文件不存在')
        # # 获取布局中的所有widget和子布局中的widget
        # index=0
        # for i in range(layout.count()):
        #     item = layout.itemAt(i)
        #     if item.layout() is not None:
        #         sub_layout = item.layout()
        #         for j in range(sub_layout.count()):
        #             sub_item = sub_layout.itemAt(j)
        #             if sub_item.widget() is not None:
        #                 sub_item.widget().setObjectName(str(index))
        #                 sub_item.widget().clicked.connect(self.on_radioButton_toggled)
        #         index+=1

        # 报警指示灯
        self.led_motorLR_1 = Indicator(self.window_debug.indicator_motorLR_1, reverse=True)
        self.led_motorLR_2 = Indicator(self.window_debug.indicator_motorLR_2, reverse=True)
        self.led_motorLR_3 = Indicator(self.window_debug.indicator_motorLR_3, reverse=True)
        self.led_motorLR_4 = Indicator(self.window_debug.indicator_motorLR_4, reverse=True)
        self.led_motorRT_1 = Indicator(self.window_debug.indicator_motorRT_1, reverse=True)
        self.led_motorRT_2 = Indicator(self.window_debug.indicator_motorRT_2, reverse=True)
        self.led_motorRT_3 = Indicator(self.window_debug.indicator_motorRT_3, reverse=True)
        self.led_motorRT_4 = Indicator(self.window_debug.indicator_motorRT_4, reverse=True)
        self.led_motorUP_1 = Indicator(self.window_debug.indicator_motorUP_1, reverse=True)
        self.led_motorUP_2 = Indicator(self.window_debug.indicator_motorUP_2, reverse=True)
        self.led_motorUP_3 = Indicator(self.window_debug.indicator_motorUP_3, reverse=True)
        self.led_motorUP_4 = Indicator(self.window_debug.indicator_motorUP_4, reverse=True)
        self.led_AGV_E1 = Indicator(self.window_debug.indicator_AGV_E1, reverse=True)
        self.led_AGV_E2 = Indicator(self.window_debug.indicator_AGV_E2, reverse=True)
        self.led_AGV_E3 = Indicator(self.window_debug.indicator_AGV_E3, reverse=True)
        self.led_AGV_E4 = Indicator(self.window_debug.indicator_AGV_E4, reverse=True)
        self.led_AGV_W1 = Indicator(self.window_debug.indicator_AGV_W1, reverse=True)
        self.led_AGV_W2 = Indicator(self.window_debug.indicator_AGV_W2, reverse=True)
        self.led_AGV_W3 = Indicator(self.window_debug.indicator_AGV_W3, reverse=True)
        self.led_AGV_W4 = Indicator(self.window_debug.indicator_AGV_W4, reverse=True)

        self.leds = [
            self.led_motorLR_1, self.led_motorLR_2, self.led_motorRT_1, self.led_motorRT_2, self.led_motorRT_3, self.led_motorRT_4,
            self.led_motorUP_1, self.led_motorUP_2, self.led_motorUP_3, self.led_motorUP_4, self.led_AGV_E1, self.led_AGV_E2, self.led_AGV_E3,
            self.led_AGV_E4, self.led_AGV_W1, self.led_AGV_W2, self.led_AGV_W3, self.led_AGV_W4
        ]
        # for i in range(len(self.leds)):
        #     self.leds[i].flash(Indicator.INACTIVE, 300)#闪烁
        # self.led_motorLR_1.active()
        # self.led_motorLR_2.inactive()
        # self.led_motorLR_3.warning()
        # self.led_motorLR_4.flash(Indicator.INACTIVE, 300)#闪烁
        self.led_oldStatus = []
        self.window_debug.input = [
            self.window_debug.input_agv_speed_pl, self.window_debug.input_agv_speed_pa, self.window_debug.input_agv_speed_al,
            self.window_debug.input_agv_speed_aa, self.window_debug.input_agv_x, self.window_debug.input_agv_y, self.window_debug.input_agv_angle,
            self.window_debug.input_agv_speed_tl, self.window_debug.input_agv_speed_ac, self.window_debug.input_motorL_speed_a,
            self.window_debug.input_motorR_speed_a, self.window_debug.input_motorL_speed_p, self.window_debug.input_motorR_speed_p,
            self.window_debug.input_agv_location_up, self.window_debug.input_pgv_tag, self.window_debug.input_pgv_x, self.window_debug.input_pgv_y,
            self.window_debug.input_pgv_angle, self.window_debug.input_imu_angle, self.window_debug.input_motorU_speed_p,
            self.window_debug.input_motorU_speed_a, self.window_debug.input_motorT_speed_p, self.window_debug.input_motorT_speed_a,
            self.window_debug.input_agv_angle_RT, self.window_debug.input_agv_location_RT
        ]
        self.window_debug.label = [
            self.window_debug.label_LRwheel1, self.window_debug.label_LRwheel2, self.window_debug.label_Err1, self.window_debug.label_Err2,
            self.window_debug.label_Err3, self.window_debug.label_Err4, self.window_debug.label_Liftor1, self.window_debug.label_Liftor2,
            self.window_debug.label_Liftor3, self.window_debug.label_Liftor4, self.window_debug.label_Rotator1, self.window_debug.label_Rotator2,
            self.window_debug.label_Rotator3, self.window_debug.label_Rotator4, self.window_debug.label_Warn1, self.window_debug.label_Warn2,
            self.window_debug.label_Warn3, self.window_debug.label_Warn4
        ]
        # endregion
        # region # 加密解密(license授权)
        self.widget_license = widget_license()
        # 定义加密模式和填充方式
        self.MODE = AES.MODE_CBC
        self.PADDING = b'\0'
        self.widget_license.btn_query_uid.clicked.connect(self.fuc_query_uid)
        self.widget_license.btn_generate_lic.clicked.connect(self.fuc_generate_lic)
        self.widget_license.btn_authorization.clicked.connect(self.fuc_authorization)
        self.widget_license.calendarWidget.clicked.connect(self.on_date_clicked)
        self.widget_license.btn_rtc_init.clicked.connect(self.fuc_rtc_init)
        self.widget_license.btn_eeprom_init.clicked.connect(self.fuc_eeprom_init)
        self.widget_license.btn_activeChip.clicked.connect(self.fuc_activeChip)
        # endregion
        # region # 调度协议转换
        self.window_tool_convertMsg = Window_tool_convertMsg()
        self.action_tool_convertMeg.triggered.connect(self.open_tool_convertMsg)
        # endregion
        # region # 程序监控
        self.widget_license.btn_query_softwareVersion.clicked.connect(self.fuc_query_softwareVersion)
        self.widget_license.checkBox_print_threadName.stateChanged.connect(self.fuc_print_threadName)
        # endregion
        # region # 临时测试
        self.w_test = w_test()
        self.w_test.pushButton_begin.clicked.connect(self.w_test_begin)
        self.w_test.pushButton_end.clicked.connect(self.w_test_end)
        self.w_test.lineEdit_time_interval.returnPressed.connect(self.w_test_change_time_interval)

        self.thread_test = thread_test()
        self.thread_test.update_text.connect(self.w_test_update_text)
        # endregion

    # region # 定义按键监听回调函数

    def on_press(self, key):
        if self.AppIsFocus == True and self.stackedWidget.currentIndex() == 0 and self.tab_widget.currentIndex() == 0:
            if self.keyIsPressed == False:
                self.keyIsPressed = True
                # print("键盘按下")
                try:
                    if key.char == 'w':
                        self.fuc_agv_moveForward()
                    elif key.char == 's':
                        self.fuc_agv_moveBack()
                    elif key.char == 'a':
                        self.fuc_agv_rotateCCW()
                    elif key.char == 'd':
                        self.fuc_agv_rotateCW()

                except AttributeError:
                    if key == keyboard.Key.space:
                        self.fuc_agv_stop()

    def on_release(self, key):
        if self.AppIsFocus == True and self.stackedWidget.currentIndex() == 0 and self.tab_widget.currentIndex() == 0:
            self.keyIsPressed = False
            # print('按键松开')

    def focusInEvent(self, event):  # 聚焦时开启键盘监听，快捷键控制AGV运动
        self.AppIsFocus = True
        # print("聚焦")

    def focusOutEvent(self, event):  # 失焦时关闭键盘监听,防止快捷键误触发AGV运动
        self.AppIsFocus = False
        # print("失焦")

    # endregion
    # region # 参数配置_上位机
    def change_protocol(self, index):
        self.protocol = self.sender().currentText()
        if self.protocol == 'My':
            sendData1 = [0xAA, 0xAA, 0xAA, 0xAA, 0xAA]
        elif self.protocol == 'TCS':
            sendData1 = [0xBB, 0xBB, 0xBB, 0xBB, 0xBB]
        # print(widget_connect.com_protocol)
        # if (self.mode_debug=="客户端" and self.widget_connect.com_protocol.currentText()=='My')\
        #     or (self.mode_debug=="串口" and self.com_protocol_1.currentText()=='My'):
        #     sendData1=[0xAA,0xAA,0xAA,0xAA,0xAA]
        # elif (self.mode_debug=="客户端" and self.widget_connect.com_protocol.currentText()=='TCS')\
        #     or (self.mode_debug=="串口" and self.com_protocol_1.currentText()=='TCS'):
        #     sendData1=[0xBB,0xBB,0xBB,0xBB,0xBB]
        # if self.widget_connect.com_protocol.currentText()=='My':
        #     sendData1=[0xAA,0xAA,0xAA,0xAA,0xAA]
        # elif self.widget_connect.com_protocol.currentText()=='TCS':
        #     sendData1=[0xBB,0xBB,0xBB,0xBB,0xBB]

        sendData2 = bytes(sendData1)
        if self.widget_connect.check_connect_device.isChecked():
            global_s.send(sendData2)
        elif self.rxflag:
            self.ser.write(sendData2)
        elif self.flag_server:
            self.server_thread.signal_send_data.emit(sendData2)
        if (self.widget_log.check_showsSendMessage.isChecked()):  # 日志打印发送数据
            self.fuc_log("Tx:" + "  " + HexStrAddSpace(sendData2.hex()))

    def change_debugMode(self, debugMode):
        # print(debugMode)
        # print("hlly")
        debugMode = self.sender().text()
        self.mode_debug = debugMode
        if debugMode == "串口":
            self.show_all_widgets(self.widget_connect.layout_deviceState)
            self.show_all_widgets(self.widget_connect.gridLayout_com)
            self.hide_all_widgets(self.widget_connect.gridLayout_client)
            self.hide_all_widgets(self.widget_connect.gridLayout_server)
        elif debugMode == "客户端":
            self.show_all_widgets(self.widget_connect.layout_deviceState)
            self.show_all_widgets(self.widget_connect.gridLayout_client)
            self.hide_all_widgets(self.widget_connect.gridLayout_server)
            self.hide_all_widgets(self.widget_connect.gridLayout_com)
        elif debugMode == "服务端":
            # self.widget_connect.btn_server.setVisible(True)
            # self.btn_client.setVisible(False)
            self.show_all_widgets(self.widget_connect.gridLayout_server)
            self.hide_all_widgets(self.widget_connect.gridLayout_client)
            self.hide_all_widgets(self.widget_connect.gridLayout_com)
            self.hide_all_widgets(self.widget_connect.layout_deviceState)

    def get_index_fromTabname(self, tab_name='1'):  # 根据标签页名称获取标签页ID
        for index in range(self.tab_widget.count()):
            tabname = self.tab_widget.tabText(index)
            if tabname == tab_name:
                return index
        return 0

    def show_widget_config(self):
        # index = self.tab_widget.indexOf(self.tab_config)
        # if index == -1:
        self.tab_widget.addTab(self.tab_config, '启动参数配置')
        p_tabwidget_id = self.get_index_fromTabname('启动参数配置')
        self.tab_widget.setCurrentIndex(p_tabwidget_id)  # 显示标签页

    def fuc_config_save(self):  # 另存为配置
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/config")
        with open(filename, "w", encoding='utf-8') as f:
            found_widget = self.widget_config.findChildren(eval('QComboBox'))  # 找出所有QComboBox对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" + found_widget[i].currentText() + "\n")
            found_widget = self.widget_config.findChildren(eval('QLineEdit'))  # 找出所有QLineEdit对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" + found_widget[i].text() + "\n")
            found_widget = self.widget_config.findChildren(eval('QCheckBox'))  # 找出所有QLineEdit对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" +
                        ('True' if found_widget[i].isChecked() else 'False') + "\n")
            f.close()
        self.widget_config.info.setText(get_time() + "  " + "配置文件已保存至" + filename)

    def fuc_config_set(self):  # 保存到默认配置
        filename = parent_dir_path + "/appDoc/config/default.txt"  # 启动加载时如果检测到文件夹不存在已经创建了文件夹
        with open(filename, "w", encoding='utf-8') as f:
            found_widget = self.widget_config.findChildren(eval('QComboBox'))  # 找出所有QComboBox对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" + found_widget[i].currentText() + "\n")
            found_widget = self.widget_config.findChildren(eval('QLineEdit'))  # 找出所有QLineEdit对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" + found_widget[i].text() + "\n")
            found_widget = self.widget_config.findChildren(eval('QCheckBox'))  # 找出所有QLineEdit对象
            for i in range(len(found_widget)):
                f.write(found_widget[i].objectName() + ":" +
                        ('True' if found_widget[i].isChecked() else 'False') + "\n")
            f.close()
        self.widget_config.info.setText(get_time() + "  " + "配置文件已保存至" + filename)

    def fuc_config_load(self):  # 加载配置
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/config")
        if filename:
            with open(filename, 'r', encoding='utf-8') as f:
                lines = f.readlines()
                for line in lines:
                    if '#' in line:
                        line = line[:line.index('#')]  # 去除每行#后的内容
                    if line.strip():  # 去除字符串两端的空白字符（包括空格、换行符和制表符）
                        parameter = line.strip().split(":", 1)
                        search_string = parameter[0]
                        found_widget = self.widget_config.findChild(QLineEdit, search_string)  # 根据字符串查找widget中的对象
                        if found_widget:
                            found_widget.setText(parameter[1])
                        else:
                            found_widget = self.widget_config.findChild(QComboBox, search_string)  # 根据字符串查找widget中的对象
                            if found_widget:
                                found_widget.setCurrentText(parameter[1])  # 必须是下拉列表中的已有项
                            else:
                                found_widget = self.widget_config.findChild(QCheckBox, search_string)
                                found_widget.setChecked(True if parameter[1] == 'True' else False)

            self.fuc_log('加载完成')
        else:
            self.fuc_log('取消加载')

    # endregion
    # region # AGV内部参数监测界面
    def show_main(self):
        # self.widget_map.layout_route.addWidget(self.paint)
        self.show()

    def hide_main(self):
        self.window_debug.gridLayout.addWidget(self.paint)
        self.hide()

    def showGraph(self, state, item):
        if state:  # 添加曲线
            if item == 'agv_x':
                self.window_debug.curve_agv_x = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[0], name=item)  # symbol='o'
            elif item == 'agv_y':
                self.window_debug.curve_agv_y = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[1], name=item)
            elif item == 'agv_angle':
                self.window_debug.curve_agv_angle = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[2], name=item)
            elif item == 'agv_battery':
                self.window_debug.curve_agv_battery = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[3], name=item)
            elif item == 'agv_speed_tl':
                self.window_debug.curve_agv_speed_tl = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[4], name=item)
            elif item == 'agv_speed_ac':
                self.window_debug.curve_agv_speed_ac = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[5], name=item)
            elif item == 'agv_location_up':
                self.window_debug.curve_agv_location_up = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[6], name=item)
            elif item == 'imu_angle':
                self.window_debug.curve_imu_angle = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[7], name=item)
            elif item == 'agv_speed_pl':
                self.window_debug.curve_agv_speed_pl = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[8], name=item)
            elif item == 'agv_speed_al':
                self.window_debug.curve_agv_speed_al = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[9], name=item)
            elif item == 'agv_speed_pa':
                self.window_debug.curve_agv_speed_pa = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[10], name=item)
            elif item == 'agv_speed_aa':
                self.window_debug.curve_agv_speed_aa = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[11], name=item)
            elif item == 'motorL_speed_p':
                self.window_debug.curve_motorL_speed_p = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[12], name=item)
            elif item == 'motorL_speed_a':
                self.window_debug.curve_motorL_speed_a = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[13], name=item)
            elif item == 'motorR_speed_p':
                self.window_debug.curve_motorR_speed_p = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[14], name=item)
            elif item == 'motorR_speed_a':
                self.window_debug.curve_motorR_speed_a = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[15], name=item)
            elif item == 'motorU_speed_p':
                self.window_debug.curve_motorU_speed_p = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[16], name=item)
            elif item == 'motorU_speed_a':
                self.window_debug.curve_motorU_speed_a = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[17], name=item)
            elif item == 'motorT_speed_p':
                self.window_debug.curve_motorT_speed_p = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[18], name=item)
            elif item == 'motorT_speed_a':
                self.window_debug.curve_motorT_speed_a = self.window_debug.plot_item1.plot(
                    pen=self.window_debug.dataColor[19], name=item)

        else:  # 移除曲线
            if item == 'agv_x':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_x)
            elif item == 'agv_y':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_y)
            elif item == 'agv_speed_pl':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_pl)
            elif item == 'agv_speed_al':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_al)
            elif item == 'agv_speed_pa':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_pa)
            elif item == 'agv_speed_aa':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_aa)
            elif item == 'motorL_speed_p':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorL_speed_p)
            elif item == 'motorL_speed_a':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorL_speed_a)
            elif item == 'motorR_speed_p':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorR_speed_p)
            elif item == 'motorR_speed_a':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorR_speed_a)
            elif item == 'motorU_speed_p':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorU_speed_p)
            elif item == 'motorU_speed_a':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorU_speed_a)
            elif item == 'motorT_speed_p':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorT_speed_p)
            elif item == 'motorT_speed_a':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_motorT_speed_a)
            elif item == 'agv_speed_tl':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_tl)
            elif item == 'agv_speed_ac':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_speed_ac)
            elif item == 'agv_location_up':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_location_up)
            elif item == 'imu_angle':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_imu_angle)
            elif item == 'agv_angle':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_angle)
            elif item == 'agv_battery':
                self.window_debug.plot_item1.removeItem(self.window_debug.curve_agv_battery)

    def show_config(self):
        self.widget_graphConfig.show()

    def showColorDialog(self):
        sender = self.sender()
        col = QColorDialog.getColor()
        print(col.name(), "\n")
        self.window_debug.dataColor[int(sender.objectName())] = col.name()
        if col.isValid():
            print('hlly')
            sender.setStyleSheet('QWidget {background-color:%s}' % col.name())
            file_path = parent_dir_path + "/appDoc/config/array_color.txt"
            with open(file_path, 'w') as f:
                for item in self.window_debug.dataColor:
                    f.write("%s\n" % item)

    def on_radioButton_toggled(self, index):
        sender = self.sender()
        self.window_debug.dataColor[int(sender.objectName())] = sender.color
        file_path = parent_dir_path + "/appDoc/config/array_color.txt"
        with open(file_path, 'w') as f:
            for item in self.window_debug.dataColor:
                f.write("%s\n" % item)

        parent_widget = self.sender.widget()
        print(parent_widget)
        # # 检查并打印父对象
        # if parent_widget:
        #     print("Button's parent:", parent_widget)
        # else:
        #     print("Button has no parent.")

    def close_watch(self):  # 关闭监测界面时触发
        self.action_watch.setChecked(False)

    # endregion
    # region # 界面显示/隐藏控制
    def add_tab(self, title):
        tab = QWidget()
        layout = QVBoxLayout()

        label = QLabel(title)
        layout.addWidget(label)

        button = QPushButton("关闭")
        button.clicked.connect(lambda: self.close_tab(tab))
        layout.addWidget(button)

        tab.setLayout(layout)
        self.tab_widget.addTab(tab, title)

    def changeTabName(self, index):  # 双击设置选项卡名称
        new_name, ok = QInputDialog.getText(self.tab_widget, "修改名称", "输入新名称:")
        if ok and new_name:
            self.tab_widget.setTabText(index, new_name)

    def deltab(self, index):  # 删除选项卡
        print(self.tab_widget.currentIndex())
        print(self.tab_widget.tabText(index))

    def on_tab_changed(self, index):  # 改变公共组件（连接和日志区）（选项卡切换触发时）
        tabname = self.tab_widget.tabText(index)
        if tabname == "远程升级":
            self.widget_OTA.layout_connect.addWidget(self.widget_connect)
            self.widget_OTA.layout_log.addWidget(self.widget_log)
        elif tabname == "控制板测试":
            self.widget_testBoard.layout_connect.addWidget(self.widget_connect)
            self.widget_testBoard.layout_log.addWidget(self.widget_log)
        elif tabname == "CTU":
            self.widget_ctu_up.layout_connect.addWidget(self.widget_connect)
            self.widget_ctu_up.layout_log.addWidget(self.widget_log)
        elif tabname == "清洗机":
            self.widget_clean.layout_connect.addWidget(self.widget_connect)
            self.widget_clean.layout_log.addWidget(self.widget_log)
        elif tabname == "LED":
            self.widget_leds.layout_connect.addWidget(self.widget_connect)
            self.widget_leds.layout_log.addWidget(self.widget_log)
        elif tabname == "调度监测":
            self.widget_tcs_watch.layout_connect.addWidget(self.widget_connect)
            self.widget_tcs_watch.layout_log.addWidget(self.widget_log)
        elif tabname == "测试":
            self.w_test.layout_connect.addWidget(self.widget_connect)
            self.w_test.layout_log.addWidget(self.widget_log)
        else:  # 主界面添加连接和日志区
            self.layout_log.addWidget(self.widget_log)
            self.layout_connect.addWidget(self.widget_connect)

    def fuc_change_stack(self):  # 左侧选项卡选择
        current_index = self.bar_left.currentRow()
        tabname = self.bar_left.currentItem().text()
        if tabname == "远程升级":
            self.tab_widget.addTab(self.widget_OTA, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "二维码标定":
            self.tab_widget.addTab(self.widget_tags, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "陀螺仪数据":
            self.tab_widget.addTab(self.widget_imu, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "IO输入输出":
            self.tab_widget.addTab(self.widget_IO, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "控制板测试":
            self.tab_widget.addTab(self.widget_testBoard, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "清洗机":
            self.tab_widget.addTab(self.widget_clean, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "清洗机驱动板":
            self.tab_widget.addTab(self.widget_clean_driver, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "CTU":
            self.tab_widget.addTab(self.widget_ctu_up, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "LED":
            self.tab_widget.addTab(self.widget_leds, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "调度监测":
            self.tab_widget.addTab(self.widget_tcs_watch, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "日志分析":
            self.tab_widget.addTab(self.widget_log_analyse, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "WIFI参数":
            self.tab_widget.addTab(self.widget_wifi, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "语音播报":
            self.tab_widget.addTab(self.widget_audio, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "license授权":
            self.tab_widget.addTab(self.widget_license, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "测试":
            self.tab_widget.addTab(self.w_test, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "MQTT客户端":
            self.tab_widget.addTab(self.w_mqtt, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        elif tabname == "standly纠偏":
            self.tab_widget.addTab(self.widget_standly, tabname)
            p_tabwidget_id = self.get_index_fromTabname(tabname)
        else:
            self.layout_log.addWidget(self.widget_log)
            self.layout_connect.addWidget(self.widget_connect)
            # 将StackedWidget的当前索引与ListWidget的当前选中项关联起来
            self.stackedWidget.setCurrentIndex(current_index)
            p_tabwidget_id = self.get_index_fromTabname("设备控制")
        self.tab_widget.setCurrentIndex(p_tabwidget_id)  # 显示标签页
        self.timer13.start(50)  # 重置窗口大小

    def close_tab(self, index):  # 关闭选项卡
        # print(tab)

        tabname = self.tab_widget.tabText(index)
        if tabname != "设备控制":
            self.tab_widget.removeTab(index)
        # index = self.tab_widget.indexOf(tab)
        # if index != -1:
        #     self.tab_widget.removeTab(index)

    def hide_all_widgets(self, layout):
        for i in range(layout.count()):
            widget = layout.itemAt(i).widget()
            if widget is not None:
                widget.setVisible(False)

    def show_all_widgets(self, layout):
        for i in range(layout.count()):
            widget = layout.itemAt(i).widget()
            if widget is not None:
                widget.setVisible(True)

    def toggle_area_log(self, checked):
        if checked:
            self.show_all_widgets(self.area_log)
            self.show_all_widgets(self.area_log_control)
            self.show_all_widgets(self.widget_log.area_log)
            self.show_all_widgets(self.widget_log.area_log_control)
        else:
            self.hide_all_widgets(self.area_log)
            self.hide_all_widgets(self.area_log_control)
            self.hide_all_widgets(self.widget_log.area_log)
            self.hide_all_widgets(self.widget_log.area_log_control)
            self.timer13.start(50)

    def toggle_area_video(self, checked):
        if checked:
            self.widget_camera.show()
            # # self.cap = cv2.VideoCapture(1)
            # self.show_all_widgets(self.area_video)
            # self.show_all_widgets(self.horizontalLayout_3)
            # self.video_thread.start()
            # self.flag_camera = 1
        else:
            self.widget_camera.hide()
            # if self.flag_camera:
            #     self.flag_camera = 0
            #     self.video_thread.stop()
            #     print("摄像头已关闭")
            # self.hide_all_widgets(self.area_video)  # 隐藏窗口
            # self.hide_all_widgets(self.horizontalLayout_3)
            # self.timer13.start(50)  # 重置窗口大小
            # # self.resize(1300, 1000)

    def toggle_window_debug(self, checked):
        if checked:
            self.window_debug.gridLayout.addWidget(self.paint)  # 添加运动轨迹绘制窗口
            self.paint.setMinimumWidth(500)  # 设置widget最小宽度
            self.window_debug.show()
        else:
            self.widget_map.layout_route.addWidget(self.paint)
            self.window_debug.hide()

    def open_tool_convertMsg(self):
        self.window_tool_convertMsg.show()
        print("hlly")

    # endregion
    # region # 打开/关闭 串口
    def fuc_OpenCom(self):
        if (self.widget_connect.btn_com.text() == '打开串口') and self.portList.currentText():
            portFullname = self.portList.currentText()  # 设置端口
            self.ser.port = portFullname[0:portFullname.find(" ")]
            self.ser.baudrate = int(self.widget_connect.com_comBaud.currentText())  # 波特率
            self.ser.bytesize = 8  # 数据位
            self.ser.parity = 'N'  # 校验位
            self.ser.stopbits = 1  # 停止位
            if self.ser.baudrate == 9600:
                self.ser.timeout = 0.018  # 超时时间（9600）   接收雷达0.016-0.018
            elif self.ser.baudrate == 115200:
                self.ser.timeout = 0.007  # 超时时间（115200）

            # self.ser.inter_byte_timeout = 0.001                              # 超时时间（9600）   接收雷达0.016-0.018
            # self.ser.set_buffer_size(rx_size=12800, tx_size=12800)

            try:
                self.ser.open()
                # 打开线程接收
                thread_com = threading.Thread(target=self.Serial)
                self.rxflag = 1
                thread_com.start()

            except serial.SerialException:
                QMessageBox.information(None, 'Open Port Error', '此串口不能正常打开！')
                return None
            if self.ser.isOpen():
                self.widget_connect.btn_com.setText('关闭串口')
                # self.widget_connect.btn_com.setStyleSheet(
                #     QPushButton{background:#9AFF9A;border-radius:5px;})

        elif self.widget_connect.btn_com.text() == '关闭串口':
            try:
                self.rxflag = 0
                self.ser.close()
            except:
                QMessageBox.critical(None, 'Open Port Error', '此串口不能正常关闭！')
                return None
            self.widget_connect.btn_com.setText('打开串口')
            # self.widget_connect.btn_com.setStyleSheet(
            #     QPushButton{background:orange;border-radius:5px;})

    def Serial(self):
        while self.rxflag:
            try:
                if self.ser.in_waiting:
                    data = self.ser.readall()  # 不能接收持续发送的数据
                    if not data or data == 'exit':
                        print("break")
                        break
                    self.signal.data_process.emit(data)
            except Exception:
                print("wrong")
                # exit()
                self.signal.print.emit("串口异常关闭")
                self.fuc_OpenCom()

    # endregion
    # region # 服务器控制（作为服务器）
    def start_server(self):  # 启动服务器
        if self.flag_server == False:
            self.widget_connect.btn_server.setText("关闭服务器")
            self.flag_server = True
            self.server_thread.start()
        else:
            self.widget_connect.btn_server.setText("开启服务器")
            self.flag_server = False
            self.server_thread.stop()
            self.widget_connect.com_clients.clear()  # 清空客户端列表

    def show_received_data(self, client_address, data):
        self.fuc_log(f"收到来自 {client_address} 的数据： {data}")

    def update_clients_list(self, flag, client_info):
        item_text = client_info[0] + ':' + str(client_info[1])
        if (self.widget_connect.com_clients.findText(item_text) == -1) & (flag == "add"):  # 没有找到
            self.widget_connect.com_clients.insertItem(0, item_text)  # 添加新项，并将新项的索引设为0
            self.widget_connect.com_clients.setCurrentIndex(0)  # 将下拉框的当前索引设置为添加的项的位置
        elif (self.widget_connect.com_clients.findText(item_text) != -1) & (flag == "remove"):
            self.widget_connect.com_clients.removeItem(self.widget_connect.com_clients.findText(item_text))

    def controlDevice_change(self, num):  # 切换当前控制设备
        self.controlDevice = self.widget_connect.com_clients.currentText()
        self.server_thread.sig_controlDevice_change.emit(self.controlDevice)

    # endregion
    # region # 连接/断开 服务器 (作为客户端)
    def connect_server(self):
        global global_flag_client, global_s
        if (global_flag_client == 0):
            global_s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            try:
                global_s.settimeout(3)  # 设置socket超时时间后，没有接收到数据连接也会断开
                s_ip = self.widget_connect.com_server.currentText().split(":")[0]
                s_port = int(self.widget_connect.comboBox_serverPort.currentText())
                global_s.connect((s_ip, s_port))
                global_s.settimeout(None)  # 连接成功后重新设置

                self.fuc_log("服务器已连接")
                global_flag_client = 1
                print(global_flag_client)
                # self.widget_connect.label_device_state.setText("在线")
                thread_client = threading.Thread(target=self.server_process)
                thread_client.start()
            except socket.error as e:
                # print("连接错误:", e)
                self.fuc_log("连接失败")
                self.widget_connect.check_connect_device.setChecked(False)
                if e.errno == 111:  # Errno 111 对应 "连接被拒绝"
                    print("服务端已关闭")
            except socket.timeout:
                self.signal.show_msg.emit('连接超时！')
                # global_s.close()
            except:
                self.signal.show_msg.emit('连接失败')
                # global_s.close()
        else:

            global_flag_client = 0
            global_s.close()
            self.fuc_log("服务器已断开")

    def server_process(self):
        global global_flag_client, global_s
        while global_flag_client:
            try:
                data = global_s.recv(1024)
            except Exception as e:
                data = None
                print(e)
                print("tcp server receive err")
                global_flag_client = 0
                global_s.close()
                self.fuc_log("服务器已断开")
                self.widget_connect.check_connect_device.setChecked(False)
                break

            if not data:
                print("tcp server receive none")
                # break
            elif self.widget_connect.check_connect_device.isChecked():
                # self.fuc_data_process(data)
                self.signal.data_process.emit(data)

    def fuc_com_server_change(self):
        if self.widget_connect.com_server.currentText() == 'host':
            self.widget_connect.com_server.setCurrentText("127.0.0.1")

    def fuc_check_mode_wifiToUart(self):
        global global_flag_client
        if self.widget_connect.check_connect_device.isChecked():
            global_flag_client = 0
        else:
            global_flag_client = 1
            # self.widget_connect.label_device_state.setText("离线")
        self.connect_server()

    # endregion
    # region # 连接多个服务器 (作为客户端)
    def populate_excel_files(self, combo_box: QComboBox, directory: str):
        """
        获取指定目录下的所有 Excel 文件，并添加到 QComboBox

        :param combo_box: 要填充的 QComboBox 对象
        :param directory: 要扫描的目录路径（如 "/appDoc/list_server/"）
        """
        combo_box.clear()  # 清空现有选项

        # 检查目录是否存在
        if not os.path.exists(directory):
            print(f"错误：目录 {directory} 不存在！")
            return

        # 遍历目录，筛选 Excel 文件
        excel_files = []
        for file in os.listdir(directory):
            if file.endswith(('.xlsx', '.xls')):
                excel_files.append(file)

        # 添加到 ComboBox
        if excel_files:
            combo_box.addItems(excel_files)
        else:
            print("警告：未找到 Excel 文件！")

    def toggle_widget_servers(self, checked):
        self.widget_servers.show()

    def servers_handle(self, identify, id, data):  # 接收数据处理
        self.servers_data_restore(identify, id, data)

    def servers_data_restore(self, identify, id, data):  # 接收数据还原（去转义）
        pattern = rb'(?<!\\);'  # 如果';'前为'\'则不进行分割
        datalist = [x for x in re.split(pattern, data) if x]  # 去掉空''
        for i in datalist:
            pattern = rb'[\\](?=;)'
            i = re.sub(pattern, b"", i)  # 如果'\'后面是';'去掉'\'
            if len(i) > 1:
                # print(len(data))
                self.servers_data_classify(identify, id, i)
            else:
                print(data)

    def servers_data_classify(self, identify, id, data):  # 接收数据分类
        if (data[0] == 0x50):  # 接收十六进制数组
            a = HexStrAddSpace(data.hex())
            self.fuc_log(str(identify) + a)
        elif ((data[0] == 0x42) & (data[1] == 0x44)):  # BD（0x42,0x44）来自于控制板的数据
            self.servers_data_verify(identify, id, data)
        else:  # 接收中文或英文或其它
            if self.widget_log.check_showHex.isChecked():
                a = HexStrAddSpace(data.hex())
            else:
                try:
                    a = data.decode('utf-8')
                    print("utf-8")
                except Exception:
                    try:
                        a = data.decode('gbk')
                        print("gbk")
                    except:
                        a = str(data)
                        print("字符串")
            self.fuc_log(str(identify) + a)
        # except:
        #     self.fuc_log("接收数据分类错误")

    def servers_data_verify(self, identify, id, data):  # 接收数据验证
        # print(len(data))
        # if(len(data)>3):
        try:
            # 帧头、帧尾、长度都对
            if ((len(data) > 8) & (len(data) == data[3]) & (data[len(data) - 1] == 0xda)):
                # print("单帧")
                self.servers_data_apply(identify, id, data[4:len(data) - 2])
                # a=HexStrAddSpace(data.hex())#查看原始数据
                # self.fuc_log(str(identify)+a)
            elif ((len(data) < 300) & (len(data) > data[3])):  # 多帧数据拆分开处理
                processData = data[:data[3]]
                # self.fuc_data_verify(processData) #!!!数据合格分开后，重新验证
                # !!!数据合格分开后，重新验证，不能直接调用fuc_data_apply！！！
                self.servers_data_verify(identify, id, processData)
                remainData = data[data[3]:]
                # print("多帧")
                # print("总长度"+str(len(data))+'长度'+str(data[3]))
                self.servers_data_classify(identify, id, remainData)
                # print("剩余长度"+str(len(remainData)))
            else:
                if (data[len(data) - 1] != 0xda):
                    # self.fuc_log(str(identify)+"帧尾错误")
                    try:
                        print(data)
                        a = data.decode('utf-8')
                        print("english")
                    except Exception:
                        try:
                            # a=data.decode('gbk')
                            a = HexStrAddSpace(data.hex())
                            print("中文")
                        except:
                            a = str(data)
                    # self.fuc_log(str(identify)+HexStrAddSpace(data.hex()))
                    self.fuc_log(str(identify) + a)
                else:
                    print("数据格式错误")
                    # self.fuc_data_apply(data[4:len(data)-2])
        except:
            a = HexStrAddSpace(data.hex())
            self.fuc_log(str(identify) + a)

    def servers_data_apply(self, identify, id, data):  # 接收数据应用
        if ((data[0] == 0x00) & (data[1] == 0x00)):  # 升级进度
            data = data[2:]
            if data[0] == 0xff:
                self.fuc_log(str(identify) + "升级失败")
            else:
                lenth = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]
                try:
                    self.servers_pgBars[id].setValue(lenth)
                except Exception:
                    self.fuc_log(str(identify) + "返回数据错误")
                if self.fileLen == lenth:
                    self.fuc_log(str(identify) + "升级完成")

    def servers_load(self, file_path):  # 加载服务器IP端口
        if os.path.exists(file_path):
            try:
                # 选择工作表
                workbook = openpyxl.load_workbook(file_path)
                sheet = workbook.active
                label = QLabel('升级进度')
                self.widget_servers.gridLayout.addWidget(label, 0, 5)
                # print(sheet.max_row)
                for row in range(1, sheet.max_row):
                    # print(sheet.cell(row+1,2).value)
                    check = QCheckBox()
                    check.setChecked(True)
                    self.servers_checkBoxs.append(check)
                    self.widget_servers.gridLayout.addWidget(check, row, 0)

                    label = QLabel(str(sheet.cell(row + 1, 3).value))
                    self.widget_servers.gridLayout.addWidget(label, row, 1)
                    setattr(self, f"label_note_{row}", label)

                    for j in range(2):
                        line_edit = QLineEdit(self)
                        line_edit.setText(str(sheet.cell(row + 1, 1 + j).value))
                        self.servers_inputs.append(line_edit)
                        self.widget_servers.gridLayout.addWidget(line_edit, row, 2 + j)
                        setattr(self, f"line_edit_IpPort_{row}_{j}", line_edit)

                    label = QLabel('未连接')
                    self.servers_labels.append(label)
                    self.widget_servers.gridLayout.addWidget(label, row, 4)

                    button = QPushButton("连接")
                    button.clicked.connect(lambda _, index=row - 1: self.servers_connect(index))
                    self.servers_buttons.append(button)
                    self.widget_servers.gridLayout.addWidget(button, row, 5)

                    # slider = QSlider(Qt.Horizontal)
                    # slider.setRange(0, 100)
                    # slider.sliderReleased.connect(lambda index=row-1:self.servers_updateProgress(index))
                    # self.servers_sliders.append(slider)
                    # self.widget_servers.gridLayout.addWidget(slider,row,5)

                    pgBar = QProgressBar()
                    # pgBar.sliderReleased.connect(lambda index=row-1:self.servers_updateProgress(index))
                    self.servers_pgBars.append(pgBar)
                    self.widget_servers.gridLayout.addWidget(pgBar, row, 6)
                # 关闭Excel文件
                workbook.close()
            except:
                self.fuc_log('服务器IP文件加载错误')
        else:
            print('文件不存在')
            wb = openpyxl.Workbook()
            wb.save(file_path)

    def servers_edit(self):  # 编辑服务器IP端口表格
        directory = parent_dir_path + "/appDoc/list_server/"
        filename = self.widget_servers.comboBox_fileList.currentText()
        file_path = os.path.join(directory, filename)
        os.startfile(file_path)
        print("编辑服务器IP端口表格")

    def servers_reload(self):  # 重新加载服务器IP端口
        directory = parent_dir_path + "/appDoc/list_server/"
        filename = self.widget_servers.comboBox_fileList.currentText()
        file_path = os.path.join(directory, filename)
        try:
            # 选择工作表
            workbook = openpyxl.load_workbook(file_path)
            sheet = workbook.active

            for row in range(1, sheet.max_row):
                label = getattr(self, f"label_note_{row}")
                label.setText(str(sheet.cell(row + 1, 3).value))  # 第3列数据
                for j in range(2):
                    line_edit = getattr(self, f"line_edit_IpPort_{row}_{j}")
                    line_edit.setText(str(sheet.cell(row + 1, 1 + j).value))

            workbook.close()
        except:
            self.fuc_log('服务器IP文件加载错误')

    def servers_chooseAll(self):
        if self.widget_servers.check_all.isChecked():
            for i in range(len(self.servers_checkBoxs)):
                self.servers_checkBoxs[i].setChecked(True)
        else:
            for i in range(len(self.servers_checkBoxs)):
                self.servers_checkBoxs[i].setChecked(False)

    def servers_disConnect(self, id, socketThread):
        print('hlly_disconnect', socketThread)
        self.servers_thread.remove(socketThread)
        print(self.servers_thread)
        self.servers_labels[id].setText('已断开')
        self.servers_labels[id].setStyleSheet("color: red;")

    def servers_disConnectAll(self):
        for i in range(len(self.servers_thread)):
            self.servers_thread[i].stop()
        self.servers_thread = []
        for i in range(len(self.servers_labels)):
            self.servers_labels[i].setText('未连接')
            self.servers_labels[i].setStyleSheet("color: black;")

    def servers_connect(self, i):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.settimeout(0.3)
        s_ip = self.servers_inputs[2 * i].text()
        s_port = int(self.servers_inputs[2 * i + 1].text())
        try:
            s.connect((s_ip, s_port))
            s.settimeout(None)
            self.fuc_log(str(s.getpeername()) + "连接成功")
            self.servers_labels[i].setText('已连接')
            self.servers_labels[i].setStyleSheet("color: green;")
            socketThread = clientThread(i, s)
            socketThread.log.connect(self.fuc_log)
            socketThread.data_received.connect(self.servers_handle)
            socketThread.disConnect.connect(self.servers_disConnect)
            socketThread.start()
            self.servers_thread.append(socketThread)
            print(self.servers_thread)

        except socket.error as e:
            self.fuc_log(str(s.getpeername()) + str(e) + "连接失败")
            self.servers_labels[i].setText('未连接')
            self.servers_labels[i].setStyleSheet("color: red;")

    def servers_connectAll(self):
        # # 假设你要连接多个服务器
        # servers = [('10.0.0.156', 5001), ('10.0.0.156', 5002)]
        # # servers = [('10.0.0.156', 5001)]

        # for server in servers:
        #     worker = SocketWorker(server)
        #     worker.data_received.connect(self.servers_handle)
        #     worker.log.connect(self.fuc_log)
        #     worker.start()
        #     self.servers_thread.append(worker)
        # print(self.servers_thread)

        for i in range(len(self.servers_labels)):
            if self.servers_checkBoxs[i].isChecked():
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.settimeout(0.3)  # 设置socket超时时间后，没有接收到数据连接也会断开
                s_ip = self.servers_inputs[2 * i].text()
                s_port = int(self.servers_inputs[2 * i + 1].text())
                try:
                    s.connect((s_ip, s_port))
                    s.settimeout(None)
                    self.fuc_log(str(s.getpeername()) + "连接成功")
                    self.servers_labels[i].setText('已连接')
                    self.servers_labels[i].setStyleSheet("color: green;")
                    socketThread = clientThread(i, s)
                    socketThread.log.connect(self.fuc_log)
                    socketThread.data_received.connect(self.servers_handle)
                    socketThread.disConnect.connect(self.servers_disConnect)
                    socketThread.start()
                    self.servers_thread.append(socketThread)
                except socket.error as e:
                    self.fuc_log(str(s.getpeername()) + str(e) + "连接失败")
                    self.servers_labels[i].setText('未连接')
                    self.servers_labels[i].setStyleSheet("color: red;")

    def servers_updateProgress(self, i):
        print(self.servers_sliders[i].value())
        print(i)

    # endregion
    # region # MQTT客户端
    def mqtt_connect_server(self):  # 连接服务器
        if self.w_mqtt.status_connect == False:
            self.w_mqtt.serverIp = self.w_mqtt.input_ip.text()
            self.w_mqtt.serverPort = int(self.w_mqtt.input_port.text())
            self.w_mqtt.serverID = self.w_mqtt.input_clientID.text()
            self.w_mqtt.mqtt_thread = MqttThread(self.w_mqtt.serverIp, self.w_mqtt.serverPort, self.w_mqtt.serverID)
            # self.w_mqtt.mqtt_thread.received_data.connect(self.mqtt_show_received_data)
            self.w_mqtt.mqtt_thread.mqtt_log.connect(self.mqtt_log)
            self.w_mqtt.mqtt_thread.sig_connect_fail.connect(self.mqtt_connect_server_fail)
            self.w_mqtt.mqtt_thread.sig_data_process.connect(self.mqtt_process_data)
            try:
                self.w_mqtt.mqtt_thread.start()
                self.w_mqtt.btn_server.setText("断开服务器")
                self.w_mqtt.status_connect = True
            except Exception as e:
                self.mqtt_log(f"连接服务器失败，原因：{e}")
        else:
            self.w_mqtt.btn_server.setText("连接服务器")
            self.w_mqtt.status_connect = False
            self.w_mqtt.mqtt_thread.stop()
            # self.w_mqtt.com_clients.clear()#清空客户端列表

    def mqtt_connect_server_fail(self):
        self.w_mqtt.btn_server.setText("连接服务器")
        self.w_mqtt.status_connect = False
        self.w_mqtt.mqtt_thread.stop()

    def mqtt_subscribe_topic(self):  # 订阅话题
        if self.w_mqtt.status_connect:
            topic = self.w_mqtt.input_topic_name.text()

            if topic not in [self.w_mqtt.com_topic_list.itemText(i) for i in range(self.w_mqtt.com_topic_list.count())]:
                self.w_mqtt.com_topic_list.addItem(topic)
                self.w_mqtt.mqtt_thread.subscribe(topic)
            else:
                self.mqtt_log(f"已经订阅话题'{topic}' 。")
        else:
            self.mqtt_log("请先连接服务器")

    def mqtt_unsubscribe_topic(self):  # 取消订阅话题
        if self.w_mqtt.status_connect:
            topic = self.w_mqtt.com_topic_list.currentText()
            self.w_mqtt.mqtt_thread.unsubscribe(topic)
            self.w_mqtt.com_topic_list.removeItem(self.w_mqtt.com_topic_list.findText(topic))
        else:
            self.mqtt_log("请先连接服务器")

    def mqtt_publish_topic(self):  # 发布话题
        if self.w_mqtt.status_connect:
            topic = self.w_mqtt.input_topic_name.text()
            data = self.w_mqtt.text_topic_content.toPlainText()
            isShow = self.w_mqtt.check_show_publish.isChecked()
            self.w_mqtt.mqtt_thread.publish(topic, data, isShow)
        else:
            self.mqtt_log("请先连接服务器")

    def mqtt_show_received_data(self, client_address, data):
        self.mqtt_log(f"收到来自 {client_address} 的数据： {data}")

    def mqtt_process_data(self, topic, json_str):
        if self.w_mqtt.check_show_json.isChecked():
            try:
                # json_str = '{"name": "John", "age": 30, "city": "New York"}'
                data = json.loads(json_str)
                self.mqtt_log(f"成功解析 {topic} 的数据")
                str_data = ''
                for key, value in data.items():
                    str_data += f"{key}: {value}" + "  "
                self.mqtt_log(str_data)

                # print(data["name"])
            except json.JSONDecodeError as e:
                self.mqtt_log(f"JSON解析错误: {e}")

    def mqtt_log(self, text=""):  # 日志显示
        log = get_time() + "  " + text
        self.w_mqtt.textBrowser.append(log)

    def mqtt_clearLog(self, text=""):  # 日志显示区清空
        self.w_mqtt.textBrowser.clear()

    # endregion
    # region # 日志管理

    def fuc_log(self, text="", type="", save=False):  # 日志显示区打印
        self.signal.print.emit(text)

    def sigCall_print(self, text=""):
        log = get_time() + "  " + text
        self.widget_log.textBrowser.append(log)
        self.fuc_savelog(log)

    def fuc_openlog(self):  # 打开日志
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/log")
        if filename:
            os.startfile(filename)

    def fuc_savelog(self, text):  # 保存日志
        if self.widget_log.check_record.isChecked():
            filepath = "appDoc/log/{0}.txt".format(self.widget_log.input_record_name.text())
            with open(filepath, "a+", encoding='utf-8') as f:
                f.write("{}\n".format(text))  # 自带文件关闭功能，不需要再写f.close()

    def fuc_clear(self, text=""):  # 日志显示区清空
        self.widget_log.textBrowser.clear()
        # self.fuc_log("hlly\r\n")

    # endregion
    # region # 界面数据更新(进程处理完回调)
    def sigCall_show_msg(self, text=""):
        QMessageBox.about(self, "note", text)

    def sigCall_update_deviceStatus(self, text=""):  # 更新设备在线状态
        self.widget_connect.label_device_state.setText(text)

    def sigCall_update_angle(self, x, y):
        self.angleCurve.plot.setData(x, y)

    def sigCall_update_imuRawData(self, x, imuRawData, average):
        for i in range(6):  # 更新6条曲线数据
            self.widget_imu.curve[i].setData(x, imuRawData[i])
            self.widget_imu.average[i].setText(str(average[i]))
        gyroX = float(self.widget_imu.input_gyroX.text())
        gyroY = float(self.widget_imu.input_gyroY.text())
        gyroZ = float(self.widget_imu.input_gyroZ.text())
        sumGyro = math.sqrt((average[0] - gyroX)**2 + (average[1] - gyroY)**2 + (average[2] - gyroZ)**2)  # 对应xyz轴
        self.widget_imu.input_average_gyro.setText(str(sumGyro))
        # print(average)

    def sigCall_update_debugGraph(self, x, graphData):  # 监视AGV参数及绘制曲线
        if self.window_debug.check_agv_speed_pl.isChecked():
            self.window_debug.curve_agv_speed_pl.setData(x, graphData[0])
        if self.window_debug.check_agv_speed_pa.isChecked():
            self.window_debug.curve_agv_speed_pa.setData(x, graphData[1])
        if self.window_debug.check_agv_speed_al.isChecked():
            self.window_debug.curve_agv_speed_al.setData(x, graphData[2])
        if self.window_debug.check_agv_speed_aa.isChecked():
            self.window_debug.curve_agv_speed_aa.setData(x, graphData[3])
        if self.window_debug.check_agv_x.isChecked():
            self.window_debug.curve_agv_x.setData(x, graphData[4])
        if self.window_debug.check_agv_y.isChecked():
            self.window_debug.curve_agv_y.setData(x, graphData[5])
        if self.window_debug.check_agv_angle.isChecked():
            self.window_debug.curve_agv_angle.setData(x, graphData[6])
        # if self.window_debug.check_agv_speed_tl.isChecked():self.window_debug.curve_agv_speed_tl.setData(x, graphData[7])
        # if self.window_debug.check_agv_speed_ac.isChecked():self.window_debug.curve_agv_speed_ac.setData(x, graphData[8])
        if self.window_debug.check_motorL_speed_a.isChecked():
            self.window_debug.curve_motorL_speed_a.setData(x, graphData[9])
        if self.window_debug.check_motorR_speed_a.isChecked():
            self.window_debug.curve_motorR_speed_a.setData(x, graphData[10])
        if self.window_debug.check_motorL_speed_p.isChecked():
            self.window_debug.curve_motorL_speed_p.setData(x, graphData[11])
        if self.window_debug.check_motorR_speed_p.isChecked():
            self.window_debug.curve_motorR_speed_p.setData(x, graphData[12])
        # if self.window_debug.check_agv_location_up.isChecked():self.window_debug.curve_agv_location_up.setData(x, graphData[13])
        if self.window_debug.check_imu_angle.isChecked():
            self.window_debug.curve_imu_angle.setData(x, graphData[18])
        if self.window_debug.check_motorU_speed_p.isChecked():
            self.window_debug.curve_motorU_speed_p.setData(x, graphData[19])
        if self.window_debug.check_motorU_speed_a.isChecked():
            self.window_debug.curve_motorU_speed_a.setData(x, graphData[20])
        if self.window_debug.check_motorT_speed_p.isChecked():
            self.window_debug.curve_motorT_speed_p.setData(x, graphData[21])
        if self.window_debug.check_motorT_speed_a.isChecked():
            self.window_debug.curve_motorT_speed_a.setData(x, graphData[22])

    def sigCall_update_debug(self, list):  # 监视AGV参数及绘制曲线
        # print(list)
        for i in range(25):
            self.window_debug.input[i].setText(str(list[i]))

    def sigCall_update_debug2(self, list):  # 监视AGV参数及绘制曲线
        print(list)
        print(len(list))

        self.window_debug.input_agv_battery.setText(str(list[0]))
        status = list[1:]
        if status != self.led_oldStatus:  # 状态改变
            for i in range(18):
                if i > 1:
                    self.window_debug.label[i].setText(hex(status[i])[2:].zfill(2))
                    if status[i] == 0:
                        self.leds[i].inactive()
                    else:
                        self.leds[i].active()
                else:
                    self.window_debug.label[i].setText(status[i].hex())
                    if status[i] == 0:
                        self.leds[i].inactive()
                    else:
                        self.leds[i].active()

        self.led_oldStatus = status

    def sigCall_update_mecanum(self, x, y, p):  # 监视麦克纳姆轮参数
        self.widget_mecanum.input_feedBack_LF.setText(str(p[0]))
        self.widget_mecanum.input_feedBack_RF.setText(str(p[1]))
        self.widget_mecanum.input_feedBack_LB.setText(str(p[2]))
        self.widget_mecanum.input_feedBack_RB.setText(str(p[3]))
        for i in range(4):  # 更新6条曲线数据
            self.widget_mecanum.curve[i].setData(x, y[i])

    def sigCall_update_position(self, list):
        self.paint.scatter.setData(list)

    def sigCall_update_PGV_informaton(self, PGV_X_bias, PGV_Y_bias, PGV_Yaw, PGV_tagNuber):
        self.input_PGV_posX.setText(PGV_X_bias)
        self.input_PGV_posY.setText(PGV_Y_bias)
        self.input_PGV_posYaw.setText(PGV_Yaw)
        self.input_PGV_tagNuber.setText(PGV_tagNuber)

    def sigCall_update_infomation(self, agv_currentPosX, agv_currentPosY, agv_currentAngle, agv_ultrasonic):
        self.input_agv_currentPosX.setText(agv_currentPosX)  # 世界坐标X信息
        self.input_agv_currentPosY.setText(agv_currentPosY)  # 世界坐标Y信息
        self.input_agv_curentAngle.setText(agv_currentAngle)  # 角度信息
        self.input_agv_ultrasonic.setText(agv_ultrasonic)  # 角度信息

    def sigCall_pgB_DmCode(self, i):
        self.widget_tags.pgB_upload.setValue(i)

    # endregion
    # region # 发送数据
    def fuc_serial_sendRaw(self, data):
        global global_s
        try:
            sendData2 = bytes(data)
        except:
            print("发送数据错误")
            print(data)
            return 0
        try:
            if self.widget_connect.check_connect_device.isChecked():
                global_s.send(sendData2)
            elif self.rxflag:
                self.ser.write(sendData2)
            elif self.flag_server:
                self.server_thread.signal_send_data.emit(sendData2)
            elif len(self.servers_thread) == 0:
                self.fuc_log('设备未连接')
                return 0
            if (self.widget_log.check_showsSendMessage.isChecked()):  # 日志打印发送数据
                self.fuc_log("Tx:" + "  " + HexStrAddSpace(sendData2.hex()))
            for i in range(len(self.servers_thread)):
                self.servers_thread[i].send_message(sendData2)
        except:
            print("发送数据错误")
            print(data)
            return 0

    def fuc_serial_send(self, cmd_id, cmd_data):
        global global_s
        cmd_header = [0x50, 0x43]
        cmd_length = [0x00, 0x00]
        cmd_check = [0x00]
        cmd_end = [0xda]
        cmd_length[1] = len(cmd_data) + 8
        sendData0 = cmd_header + cmd_length + cmd_id + cmd_data
        cmd_check[0] = SumCheck(sendData0, cmd_length[1] - 2)
        sendData1 = sendData0 + cmd_check + cmd_end
        sendData2 = bytes(sendData1)

        if self.widget_connect.check_connect_device.isChecked():
            global_s.send(sendData2)
        elif self.rxflag:
            self.ser.write(sendData2)
        elif self.flag_server:
            self.server_thread.signal_send_data.emit(sendData2)
        elif len(self.servers_thread) == 0:
            self.fuc_log('设备未连接')
            return 0
        if (self.widget_log.check_showsSendMessage.isChecked()):  # 日志打印发送数据
            self.fuc_log("Tx:" + "  " + HexStrAddSpace(sendData2.hex()))
        for i in range(len(self.servers_thread)):
            self.servers_thread[i].send_message(sendData2)

    # endregion
    # region # 接收数据处理
    def fuc_data_process(self, data):
        self.deviceStatusChecker.last_heartbeat_time = QTime.currentTime()  # 收到数据，更新设备在线状态
        if (len(data) == 137):
            self.server_parse_data(data)
        elif (len(data) == 345):
            a = HexStrAddSpace(data.hex())
            self.window_tool_convertMsg.text_raw.setText(a)
            self.window_tool_convertMsg.fuc_convert()
            # self.fuc_log(a)
        elif (self.flag_voice_download):
            self.fuc_voice_downloadFeedback(data)
        else:
            self.fuc_data_restore(data)
        if self.widget_tcs_watch.checkBox_record_data.isChecked():
            self.tcs_record_data(data)

    def fuc_data_restore(self, data):  # 接收数据还原（去转义）
        # if self.fuc_data_classify(data) == False:
        #             # print(i)
        #             pass
        pattern = rb'(?<!\\);'  # 如果';'前为'\'则不进行分割
        datalist = [x for x in re.split(pattern, data) if x]  # 去掉空''
        for i in datalist:
            pattern = rb'[\\](?=;)'
            i = re.sub(pattern, b"", i)  # 如果'\'后面是';'去掉'\'
            if len(i) > 1:
                # print(len(i))
                # if len(i)<30 and i[3]==0x50:
                #     a=HexStrAddSpace(i.hex())
                #     print(a)
                if self.fuc_data_classify(i) == False:
                    # print(i)
                    pass
            else:
                print(data)

    def fuc_data_classify(self, data):  # 接收数据分类
        # print(len(data))
        # try:
        if ((data[0] == 0x50) & (data[1] == 0x43)):  # PC（0x50,0x43）来自于上位机的数据
            self.fuc_data_verify(data)
        elif (data[0] == 0x50):  # 接收十六进制数组
            a = HexStrAddSpace(data.hex())
            self.fuc_log(a)
        elif ((data[0] == 0x42) & (data[1] == 0x44)):  # BD（0x42,0x44）来自于控制板的数据
            self.data_from_board = 1
            self.fuc_data_verify(data)
        elif ((data[0] == 0x43) & (data[1] == 0x41)):  # 来自于USB转CAN
            self.data_from_board = 1
            self.fuc_data_verify(data)
        elif ((data[0] == 0x43) & (data[1] == 0x4C)):  # 来自于清洗机器人M核控制板的数据
            self.data_from_board = 0x434C
            self.fuc_data_verify(data)
        elif ((data[0] == 0x43) & (data[1] == 0x4F)):  # 来自于清洗机工作站M核控制板的数据
            self.data_from_board = 0x434F
            self.fuc_data_verify(data)
        elif ((data[0] == 0x44) & (data[1] == 0x01)):  # 来自于清洗机器人驱动板的数据
            self.data_from_board = 0x4401
            self.fuc_data_verify(data)
        elif ((data[0] == 0x43) & (data[1] == 0x54)):  # 来自于CTU上装机构M核的数据
            self.data_from_board = 0x4354
            self.fuc_data_verify(data)
        elif ((data[0] == 0x43) & (data[1] == 0x57)):  # 来自于CTU下装机构M核的数据
            self.data_from_board = 0x4357
            self.fuc_data_verify(data)
        elif ((data[0] == 0x48) & (data[1] == 0x44)):  # BD（0x42,0x44）来自于控制板的数据
            self.fuc_data_verify(data)
            # print(data)
            # a=HexStrAddSpace(data.hex())
            # self.fuc_log(a)
            # print(len(data))
        elif ((data[0] == 0x00) & (data[1] == 0x44)):  # 华睿PGV相机原始数据解析
            self.fuc_log(HexStrAddSpace(data.hex()))
            self.fuc_data_verify(data)
        elif ((data[0] == 0x03) & (data[1] == 0x04)):  # 倍加福PGV相机原始数据解析
            self.fuc_log(HexStrAddSpace(data.hex()))
            self.fuc_data_verify(data)
        # elif ((data[0] == 0x44) & (data[1] == 0x4D)):  #DM显示在二维码界面
        #     self.widget_tags.info.setText(get_time() + " " + data.decode('utf-8'))
        elif ((data[0] == 0x53) & (data[1] == 0x54)):  # 标准机M核控制板
            self.data_from_board = 1
            self.fuc_data_verify(data)
        else:  # 接收中文或英文或其它
            if self.widget_log.check_showHex.isChecked():
                a = HexStrAddSpace(data.hex())
            else:
                try:
                    a = data.decode('utf-8')
                    print("utf-8")
                except Exception:
                    try:
                        a = data.decode('gbk')
                        print("gbk")
                    except:
                        a = str(data)
                        print("字符串")
            self.fuc_log(a)
            return False
        return True
        # except:
        #     self.fuc_log("接收数据分类错误")

    def fuc_data_verify(self, data):  # 接收数据验证
        # print(len(data))
        # if(len(data)>3):
        # try:
        # 帧头、帧尾、长度都对
        if ((len(data) > 8) & (len(data) == data[3]) & (data[len(data) - 1] == 0xda)):
            # print("单帧")
            self.fuc_data_apply(data[4:len(data) - 2])
            # a=HexStrAddSpace(data.hex())#查看原始数据
            # self.fuc_log(a)
        elif ((len(data) < 300) & (len(data) > data[3])):  # 多帧数据拆分开处理
            processData = data[:data[3]]
            # self.fuc_data_verify(processData) #!!!数据合格分开后，重新验证
            # !!!数据合格分开后，重新验证，不能直接调用fuc_data_apply！！！
            self.fuc_data_verify(processData)
            remainData = data[data[3]:]
            # print("多帧")
            # print("总长度"+str(len(data))+'长度'+str(data[3]))
            self.fuc_data_classify(remainData)
            # print("剩余长度"+str(len(remainData)))
        else:
            if (data[len(data) - 1] != 0xda):
                # self.fuc_log("帧尾错误")
                try:
                    print(data)
                    a = data.decode('utf-8')
                    print("english")
                except Exception:
                    try:
                        # a=data.decode('gbk')
                        a = HexStrAddSpace(data.hex())
                        print("中文")
                    except:
                        a = str(data)
                # self.fuc_log(HexStrAddSpace(data.hex()))
                self.fuc_log(a)
            else:
                print("数据格式错误")
                # self.fuc_data_apply(data[4:len(data)-2])
        # except:
        #     a = HexStrAddSpace(data.hex())
        #     self.fuc_log(a)

    def fuc_data_apply(self, data):  # 接收数据应用
        if (self.data_from_board == 1):
            if ((data[0] == 0x00) & (data[1] == 0x00)):  # 升级进度
                data = data[2:]
                if data[0] == 0xff:
                    self.fuc_log("升级失败")
                else:
                    lenth = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]
                    try:
                        self.widget_OTA.pgB_upgrade.setValue(lenth)
                    except Exception:
                        self.fuc_log("返回数据错误")
                    if self.fileLen == lenth:
                        self.fuc_log("升级完成")
            elif ((data[0] == 0x00) & (data[1] == 0x10)):  # 控制板一键测试
                data = data[2:]
                return_state = True
                for i in range(3):
                    for j in range(8):
                        state = (data[i] >> (7 - j) & 0x01)
                        self.widget_testBoard.IO[i*8+j].setChecked(state)
                        if not state:
                            return_state = False

                data = data[4:]
                print(data)
                for i in range(4):
                    for j in range(2):
                        state = (data[0] >> (2*i+j) & 0x01)
                        self.widget_testBoard.communicate[i*2+j].setChecked(state)
                        if not state:
                            return_state = False
                # 定义 7 种状态对应的样式
                styles = {
                    0: ("测试失败", "color: red; background-color: #ffebeb;"),  # 红色文字，浅红背景
                    1: ("测试成功", "color: green; background-color: #e6ffe6;"),  # 绿色文字，浅绿背景
                    2: ("测试中", "color: blue; background-color: #e6f2ff;"),
                    3: ("警告", "color: #ff9900; background-color: #fff3e6;"),
                    4: ("等待", "color: purple; background-color: #f0e6ff;"),
                    5: ("运行中", "color: teal; background-color: #e6ffff;"),
                    6: ("已完成", "color: #ff33cc; background-color: #ffe6ff;"),
                }

                # 根据 return_state 设置样式
                if return_state in styles:
                    text, style = styles[return_state]
                    self.widget_testBoard.label_2.setText(text)
                    self.widget_testBoard.label_2.setStyleSheet(style)

            elif ((data[0] == 0x12) & (data[1] == 0x01)):  # 士腾控制板程序监测
                agv_data1 = data[2:]
                self.thread_debug.fuc_updateData1(agv_data1)
            elif ((data[0] == 0x12) & (data[1] == 0x02)):  # 士腾控制板程序监测
                agv_data2 = data[2:]
                self.thread_debug.fuc_updateData2(agv_data2)
            elif ((data[0] == 0x30) & (data[1] == 0x00)):  # 上报芯片UID
                data = data[2:]
                uid1 = data[:4][::-1].hex()
                uid2 = data[4:8][::-1].hex()
                uid3 = data[8:12][::-1].hex()
                self.widget_license.input_license_id1.setText(uid1)
                self.widget_license.input_license_id2.setText(uid2)
                self.widget_license.input_license_id3.setText(uid3)
            elif ((data[0] == 0x30) & (data[1] == 0x01)):  # 上报芯片XID(UID偏移得到)
                data = data[2:]
                uid1 = data[:4][::-1].hex()
                uid2 = data[4:8][::-1].hex()
                uid3 = data[8:12][::-1].hex()
                self.widget_license.input_license_xid1.setText(uid1)
                self.widget_license.input_license_xid2.setText(uid2)
                self.widget_license.input_license_xid3.setText(uid3)
                # print("设备ID:"+HexStrAddSpace(uid1.hex()+uid2.hex()+uid3.hex()))
                # a=HexStrAddSpace(data.hex())
                # self.fuc_log(a)
            elif ((data[0] == 0x30) & (data[1] == 0x02)):  # 上报自身ID
                data = data[2:]
                self.widget_connect.label_device_id.setText(str(data[3] << 8 | data[4]).zfill(4))
                # self.fuc_log("设备ID:"+HexStrAddSpace(data.hex()))
            elif ((data[0] == 0x40) & (data[1] == 0x01)):  # 上位机监控数据
                data = data[2:]
                # print(len(data))
                agv_currentLineSpeed = listToFloat(data[0:4])
                agv_currentRateSpeed = listToFloat(data[4:8])
                agv_currentPosX = round(listToFloat(data[16:20]), 3)
                agv_currentPosY = round(listToFloat(data[20:24]), 3)
                agv_currentAngle = round(listToFloat(data[24:28]), 3)

                self.input_agv_curentLineSpeed.setText(str(agv_currentLineSpeed))
                self.input_agv_curentRateSpeed.setText(str(agv_currentRateSpeed))
                self.input_agv_currentPosX.setText(str(agv_currentPosX))  # 世界坐标X信息
                self.input_agv_currentPosY.setText(str(agv_currentPosY))  # 世界坐标Y信息
                self.input_agv_curentAngle.setText(str(agv_currentAngle))  # 角度信息

                # self.thread_position.update(agv_currentPosX, agv_currentPosY)  # 更新运动轨迹
                self.thread_angle.fuc_updateAngle(agv_currentAngle)  # 更新角度曲线的数据(通过线程处理数据(数组左移)）
                if (len(data) >= 32):  # 超声波距离信息
                    agv_ultrasonic = listToFloat(data[28:32])
                    self.input_agv_ultrasonic.setText(str(agv_ultrasonic))
                if (len(data) >= 34):  # 电池电压信息
                    battery_voltage = (data[32] << 8 | data[33]) / 1000
                    self.input_battery_voltage.setText(str(battery_voltage))
                if (len(data) >= 36):  # 电池剩余电量信息
                    battery_leftEnergy = data[34] << 8 | data[35]
                    self.input_battery_energy.setText(str(battery_leftEnergy))
                if (len(data) >= 40):  # 左电机反馈速度
                    motorLeftSpeed = listToFloat(data[36:40])
                    self.input_motorL_speed.setText(str(motorLeftSpeed))
                if (len(data) >= 44):  # 右电机反馈速度
                    motorRightSpeed = listToFloat(data[40:44])
                    self.input_motorR_speed.setText(str(motorRightSpeed))
                if (len(data) >= 48):  # 左电机反馈位置
                    motorLeftPosition = listToFloat(data[44:48])
                    self.input_motorL_position.setText(str(motorLeftPosition))
                if (len(data) >= 52):  # 右电机反馈位置
                    motorRightPosition = listToFloat(data[48:52])
                    self.input_motorR_position.setText(str(motorRightPosition))
                if (len(data) >= 56):  # 旋转电机反馈速度
                    motorRotatorSpeed = listToFloat(data[52:56])
                    self.input_motorT_speed.setText(str(motorRotatorSpeed))
                    self.input_motorT_speed_2.setText(str(motorRotatorSpeed))
                if (len(data) >= 60):  # 顶升电机反馈速度
                    motorUpSpeed = listToFloat(data[56:60])
                    self.input_motorU_speed.setText(str(motorUpSpeed))
                    self.input_motorU_speed_2.setText(str(motorUpSpeed))
                if (len(data) >= 64):  # 旋转电机反馈位置
                    motorRotatorPosition = listToFloat(data[60:64])
                    self.input_motorT_position.setText(str(motorRotatorPosition))
                    self.input_motorT_position_2.setText(str(motorRotatorPosition))
                if (len(data) >= 68):  # 顶升电机反馈位置
                    motorUpPosition = listToFloat(data[64:68])
                    self.input_motorU_position.setText(str(motorUpPosition))
                    self.input_motorU_position_2.setText(str(motorUpPosition))
                if (len(data) >= 72):  # 输入状态反馈
                    inputor = data[68:72]
                    for i in range(8):
                        self.led_inputors[i].show(inputor[0] >> (7 - i) & 0x01)
                    for i in range(8, 16):
                        self.led_inputors[i].show(inputor[1] >> (15 - i) & 0x01)
                    for i in range(16, 24):
                        self.led_inputors[i].show(inputor[2] >> (23 - i) & 0x01)
                    for i in range(24, 28):
                        self.led_inputors[i].show(inputor[3] >> (31 - i) & 0x01)
                if (len(data) >= 73):  # 指令状态
                    state = data[72]
                    self.widget_tcs_watch.checkBox_flag_TM.setChecked(state >> 7 & 0x01)
                    self.widget_tcs_watch.checkBox_flag_HL.setChecked(state >> 6 & 0x01)
                    self.widget_tcs_watch.checkBox_flag_RA.setChecked(state >> 5 & 0x01)
                    self.widget_tcs_watch.checkBox_flag_RV.setChecked(state >> 4 & 0x01)
                    self.widget_tcs_watch.checkBox_flag_GP.setChecked(state >> 3 & 0x01)
                    self.widget_tcs_watch.checkBox_flag_ST.setChecked(state >> 2 & 0x01)
                if (len(data) >= 74):  # 输入信号
                    state = data[73]
                    self.widget_tcs_watch.checkBox_manual.setChecked(not (state >> 7 & 0x01))
                    self.widget_tcs_watch.checkBox_brake.setChecked(state >> 6 & 0x01)
                    self.widget_tcs_watch.checkBox_reset.setChecked(state >> 5 & 0x01)
                    self.widget_tcs_watch.checkBox_estop.setChecked(state >> 4 & 0x01)
                    self.widget_tcs_watch.checkBox_collision.setChecked(state >> 3 & 0x01)
                    self.widget_tcs_watch.checkBox_radarF1.setChecked(state >> 2 & 0x01)
                    self.widget_tcs_watch.checkBox_radarB1.setChecked(state >> 1 & 0x01)
                    self.widget_tcs_watch.checkBox_cameraF1.setChecked(state >> 0 & 0x01)
                if (len(data) >= 75):  # 警告信息
                    flag_protection = data[74]
                    warning_messages = {
                        0: ("正常", "green"),
                        1: ("警告：超过2.5m未扫到二维码", "orange"),
                        2: ("警告：纠偏角度大于30度", "red"),
                        3: ("警告：纠偏距离大于20度", "red"),
                        5: ("错误：二维码数据错误", "red"),
                        7: ("警告：重新开始任务时未扫到二维码", "orange"),
                        10: ("紧急：避障雷达触发", "purple"),
                    }
                    text, color = warning_messages.get(flag_protection, ("未知状态", "gray"))
                    self.widget_tcs_watch.label_warn.setText(str(flag_protection) + ": " + text)
                    self.widget_tcs_watch.label_warn.setStyleSheet(f"color: {color};")  # 动态设置文本颜色
                if (len(data) >= 80):  # 二维码角度修正值
                    angle_rectify = listToFloat(data[76:80])
                    if self.angle_rectify_last:
                        angle_rectify_err = angle_rectify - self.angle_rectify_last
                        if angle_rectify_err < -360:
                            angle_rectify_err += 360
                        if angle_rectify_err > 360:
                            angle_rectify_err -= 360
                        # if abs(angle_rectify_err-0)>0.00001:
                        if abs(angle_rectify_err - 0) > float(self.lineEdit_angle_rectify_th.text()):
                            self.fuc_log("X: %.2f, Y: %.2f, 二维码角度修正值：%.2f,相邻二维码误差：%.5f" %
                                        (agv_currentPosX, agv_currentPosY, angle_rectify, angle_rectify_err))
                            self.paint.dotdata.append({
                                'pos': (float(f"{agv_currentPosX:.2f}"), float(f"{agv_currentPosY:.2f}")),
                                'brush': QBrush(QColor('blue')),  # 等价于 pg.mkBrush('b')
                                'pen': 'b',  # 画笔颜色
                                # 可选附加数据（如时间戳）
                                'data': f"x: {agv_currentPosX}, y: {agv_currentPosY}, yaw: {angle_rectify_err}"
                            })
                            self.paint.scatter.setData(self.paint.dotdata)
                    self.angle_rectify_last = angle_rectify
                if (len(data) >= 83):  # 当前避障扫区
                    radarF1 = data[80]
                    radarB1 = data[81]
                    cameraF1 = data[82]
                    self.widget_tcs_watch.lineEdit_radarF1.setText(str(radarF1))
                    self.widget_tcs_watch.lineEdit_radarB1.setText(str(radarB1))
                    self.widget_tcs_watch.lineEdit_cameraF1.setText(str(cameraF1))

            elif ((data[0] == 0x40) & (data[1] == 0x02)):  # 控制板数据测试预留
                data = data[2:]  # 测试预留
            elif ((data[0] == 0x40) & (data[1] == 0x03)):  # 更新计算目标角度
                data = data[2:]
                angle = [data[0], data[1], data[2], data[3]]
                angle = listToFloat(angle)
                self.input_agv_targetAngle.setText(str(angle))
            elif ((data[0] == 0x40) & (data[1] == 0x04)):  # 更新计算目标距离
                data = data[2:]
                targetDistance = [data[0], data[1], data[2], data[3]]
                targetDistance = listToFloat(targetDistance)
                self.input_agv_targetDistance.setText(str(targetDistance))
            elif ((data[0] == 0x40) & (data[1] == 0x05)):  # 更新PGV相机信息
                return
                data = data[2:]
                # x
                PGV_X_bias = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]
                if (data[0] >> 7):
                    PGV_X_bias = PGV_X_bias - 0x100000000
                # y
                PGV_Y_bias = data[4] << 8 | data[5]
                if (data[4] >> 7):
                    PGV_Y_bias = PGV_Y_bias - 0x10000
                # w
                PGV_Yaw = data[6] << 8 | data[7]
                if (data[6] >> 7):
                    PGV_Yaw = PGV_Yaw - 0x10000
                PGV_Yaw = round(PGV_Yaw / 10, 1)
                # number
                # PGV_tagNuber = data[8] << 24 & 0xff | data[9] << 16 & 0xff | data[10] << 8 & 0xff | data[11] & 0xff
                PGV_X = int(listToFloat(data[8:12]))
                PGV_Y = int(listToFloat(data[12:16]))
                # 更新PGV数据
                self.input_PGV_posX.setText(str(PGV_X_bias))
                self.input_PGV_posY.setText(str(PGV_Y_bias))
                self.input_PGV_posYaw.setText(str(PGV_Yaw))
                self.input_PGV_X.setText(str(PGV_X))
                self.input_PGV_Y.setText(str(PGV_Y))
                # 更新角度曲线的数据
                # self.angleCurve.y1[:-1] = self.angleCurve.y1[1:]
                # self.angleCurve.y1[-1] = PGV_Yaw
                # self.angleCurve.plot1.setData(self.angleCurve.x, self.angleCurve.y1)
            elif ((data[0] == 0x40) & (data[1] == 0x06)):  # 更新料架相机数据
                data = data[2:]
                # x
                PGV_X_bias = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]
                if (data[0] >> 7):
                    PGV_X_bias = PGV_X_bias - 0x100000000
                # y
                PGV_Y_bias = data[4] << 8 | data[5]
                if (data[4] >> 7):
                    PGV_Y_bias = PGV_Y_bias - 0x10000
                # w
                PGV_Yaw = data[6] << 8 | data[7]
                if (data[6] >> 7):
                    PGV_Yaw = PGV_Yaw - 0x10000
                PGV_Yaw = round(PGV_Yaw / 10, 1)
                # number
                PGV_tagNuber = data[8] << 24 & 0xff | data[9] << 16 & 0xff | data[10] << 8 & 0xff | data[11] & 0xff

                # 更新PGV数据
                self.input_PGV_posX_up.setText(str(PGV_X_bias))
                self.input_PGV_posY_up.setText(str(PGV_Y_bias))
                self.input_PGV_posYaw_up.setText(str(PGV_Yaw))
                self.input_PGV_tagNuber_up.setText(str(PGV_tagNuber))

                # 计算料架角度
                angle_down = float(self.input_PGV_posYaw.text())
                angle_up = float(self.input_PGV_posYaw_up.text())
                angle = angle_up + angle_down
                angle = round(angle, 2)
                if angle > 360:
                    angle = angle - 360
                elif angle < -360:
                    angle = angle + 360
                self.lineEdit_rack_angle.setText(str(angle))
                # agv_currentPosX=listToFloat(data[0:4])
                # agv_currentPosY=listToFloat(data[4:8])
                # 更新运动轨迹
                # if((self.last_agv_currentPosX!=agv_currentPosX)|(self.last_agv_currentPosY!=agv_currentPosY)):
                #     self.paint.dotdata.append({'pos': (agv_currentPosX, agv_currentPosY),'brush': 'r','pen':'r'})  # 新增点
                #     self.paint.scatter.setData(self.paint.dotdata)
                # self.last_agv_currentPosX=agv_currentPosX
                # self.last_agv_currentPosY=agv_currentPosY
            elif ((data[0] == 0x40) & (data[1] == 0x07)):  # 监测陀螺仪原始数据
                imuRawData = data[2:]
                self.thread_angle.fuc_updateImuRaw(imuRawData)  # 更新角度曲线的数据(通过线程处理数据(数组左移)）

            elif ((data[0] == 0x40) & (data[1] == 0x10)):  # 读取flash参数1-7
                data = data[2:]
                if len(data) == (len(self.agv_parameter)) * 4:  # 每个参数占4个字节
                    for i in range(len(self.agv_parameter)):
                        found_widget = self.widget_agvParameter.findChild(
                            QLineEdit, self.agv_parameter[i])  # 根据字符串查找widget中的对象
                        if i == 0:  # 第一个参数为版本号，是整数不是小数
                            found_widget.setText(str(data[4 * i]))
                        else:
                            found_widget.setText(str(listToFloat(data[4 * i:4 + 4 * i])))
                    self.fuc_log('参数读取完成')
                    self.btn_read_flashParameter.setText("读取参数")
                else:
                    print('接收长度错误')
            elif ((data[0] == 0x40) & (data[1] == 0x11)):  # 读取flash参数8-14
                data = data[2:]
                # print(data)
                imu_integralGain = listToFloat(data[0:4])
                imu_GyroOffset_Z = listToFloat(data[4:8])
                agv_angleRangeOffset = listToFloat(data[8:12])
                agv_distanceRangeOffset = listToFloat(data[12:16])
                factor_rectify = listToFloat(data[16:20])
                rectifyAngle = listToFloat(data[20:24])
                self.input_imu_integralGain.setText(str(imu_integralGain))
                self.input_imu_offsetZ.setText(str(imu_GyroOffset_Z))
                self.input_angleRangeOffset.setText(str(agv_angleRangeOffset))
                self.input_distanceRangeOffset.setText(str(agv_distanceRangeOffset))
                self.input_factor_rectify.setText(str(factor_rectify))
                self.input_rectifyAngle.setText(str(rectifyAngle))
                self.btn_read_flashParameter.setText("读取参数")
            elif ((data[0] == 0x40) & (data[1] == 0x12)):  # 读取flash参数15-21
                data = data[2:]
                # print(data)
                leftWheel_compensation = listToFloat(data[0:4])
                rightWheel_compensation = listToFloat(data[4:8])
                leftWheel_offset = listToFloat(data[8:12])
                rightWheel_offset = listToFloat(data[12:16])
                self.input_LW_compensation.setText(str(leftWheel_compensation))
                self.input_RW_compensation.setText(str(rightWheel_compensation))
                self.input_LW_offset.setText(str(leftWheel_offset))
                self.input_RW_offset.setText(str(rightWheel_offset))
            elif ((data[0] == 0x40) & (data[1] == 0x20)):  # 接收二维码标定数据
                data = data[2:]
                print(data)
                DM_tagNumber = listToInt32_little(data[0:4])
                a = HexStrAddSpace(data[0:4].hex())
                print(a)
                DM_posX = listToFloat(data[4:8])
                DM_posY = listToFloat(data[8:12])
                DM_ErrorAngle = listToFloat(data[12:])

                # 创建新页面
                if self.DMcode_number % self.page_codeNum == 0:
                    self.new_page = QWidget()
                    self.grid_layout = QGridLayout()
                    self.new_page.setLayout(self.grid_layout)
                    self.widget_tags.stack_tags.addWidget(self.new_page)
                    # labelList=["","序号","二维码编码","坐标X","坐标Y","补偿角度"]
                    labelList = ["", "序号", "二维码编码", "坐标X", "坐标Y", "补偿角度"]
                    for i in range(3):
                        for j in range(6):
                            label = QLabel(labelList[j])
                            self.grid_layout.addWidget(label, 0, 6 * i + j)

                # 创建输入框
                check = QCheckBox()
                check.setChecked(True)
                self.checkBoxs.append(check)
                label = QLabel(str(self.DMcode_number + int(self.widget_tags.input_start_address.text())))
                self.dmcode_labels.append(label)
                self.grid_layout.addWidget(check, self.DMcode_number % self.page_codeNum //
                                           3 + 1, 6 * (self.DMcode_number % self.page_codeNum % 3))
                self.grid_layout.addWidget(label, self.DMcode_number % self.page_codeNum // 3 + 1,
                                           6 * (self.DMcode_number % self.page_codeNum % 3) + 1)
                for j in range(4):
                    line_edit = QLineEdit(self)
                    line_edit.setObjectName(f'line_edit_{4*self.DMcode_number+j}')
                    self.line_edits.append(line_edit)
                    self.grid_layout.addWidget(line_edit, self.DMcode_number % self.page_codeNum // 3 + 1,
                                               6 * (self.DMcode_number % self.page_codeNum % 3) + j + 2)

                self.line_edits[4 * self.DMcode_number].setText(str(DM_tagNumber))
                self.line_edits[4 * self.DMcode_number + 1].setText(str(DM_posX))
                self.line_edits[4 * self.DMcode_number + 2].setText(str(DM_posY))
                self.line_edits[4 * self.DMcode_number + 3].setText(str(DM_ErrorAngle))
                self.DMcode_number += 1
            elif ((data[0] == 0x40) & (data[1] == 0x40)):  # 路径跟踪参数
                data = data[2:]
                K_Cross = listToFloat(data[0:4])
                K_LowVelo = listToFloat(data[4:8])
                K_trace_angle = listToFloat(data[8:12])
                K_cross_angle = listToFloat(data[12:16])
                self.widget_standly.lineEdit_K_Cross.setText(str(K_Cross))
                self.widget_standly.lineEdit_K_LowVelo.setText(str(K_LowVelo))
                self.widget_standly.lineEdit_K_trace_angle.setText(str(K_trace_angle))
                self.widget_standly.lineEdit_K_cross_angle.setText(str(K_cross_angle))
            elif ((data[0] == 0x40) & (data[1] == 0x60)):  # 差速轮速度
                data = data[2:]
                currentLineSpeed = listToFloat(data[0:4])
                currentRadSpeed = listToFloat(data[4:8])
                self.input_agv_curentLineSpeed.setText(str(currentLineSpeed))
                self.input_agv_curentRateSpeed.setText(str(currentRadSpeed))
            elif ((data[0] == 0x42) & (data[1] == 0x00)):  # 更新扩展输入口状态
                self.check_input_1.setChecked(True if ((data[2] & 0x01) > 0) else False)
                self.check_input_2.setChecked(True if ((data[2] & 0x02) > 0) else False)
                self.check_input_3.setChecked(True if ((data[2] & 0x04) > 0) else False)
                self.check_input_4.setChecked(True if ((data[2] & 0x08) > 0) else False)
                self.check_input_5.setChecked(True if ((data[2] & 0x10) > 0) else False)
                self.check_input_6.setChecked(True if ((data[2] & 0x20) > 0) else False)
                self.check_input_7.setChecked(True if ((data[2] & 0x40) > 0) else False)
                self.check_input_8.setChecked(True if ((data[2] & 0x80) > 0) else False)
            elif ((data[0] == 0x42) & (data[1] == 0x09)):  # CAN通信反馈(电机、IMU)
                self.fuc_log(HexStrAddSpace(data[2:].hex()))
                canId = data[2] << 8 | data[3]
                canData = data[4:]
                if (canId == 0x700 + int(self.input_motor_nodeId.text())):
                    if (canData[0] == 0x00):
                        self.label_motor_heartState.setText("启动状态")
                    if (canData[0] == 0x02):
                        self.label_motor_heartState.setText("连接状态")
                    if (canData[0] == 0x04):
                        self.label_motor_heartState.setText("停止状态")
                    if (canData[0] == 0x05):
                        self.label_motor_heartState.setText("操作状态")
                    if (canData[0] == 0x7F):
                        self.label_motor_heartState.setText("预操作状态")
                        self.label_motor_active.setText("已激活")
                # if(canId==0x180+int(self.input_motor_nodeId.text())):
                #     speed=canData[1]<<8|canData[0]
                #     if(canData[1]>>7):speed-=0x10000
                #     self.label_motor_speed.setText(str(int(speed))+'rpm')
                if (canId == 0x585):  # 陀螺仪数据
                    if (canData[3] == 0X01):
                        angle = [canData[4], canData[5], canData[6], canData[7]]
                        angle = listToFloat(angle)
                        self.input_agv_curentAngle.setText(str(angle))
                    if (canData[3] == 0X02):
                        anglerRate = canData[4] + canData[5] << 8 + \
                            canData[6] << 16 + canData[7] << 24

                        print(anglerRate)
                        # self.input_imu_angleRate.setText(str(anglerRate)+'rad/s')
            elif ((data[0] == 0x42) & (data[1] == 0x20)):  # 处理读码相机数据
                self.sigCall_process_PGVData(data[2:])
            elif ((data[0] == 0x70) & (data[1] == 0x01)):  # 麦克纳姆小车_轮速
                data = data[2:]
                self.thread_mecanum.fuc_updateData(data)
            elif ((data[0] == 0x70) & (data[1] == 0x02)):  # 麦克纳姆小车_PID参数
                data = data[2:]
                for i in range(12):
                    self.pid_inputs[i].setText(str(listToFloat_B(data[4 * i:4 * i + 4])))
            elif ((data[0] == 0x80) & (data[1] == 0x01)):  # 手柄采样数据
                data = data[2:]
                lx = listToInt16(data[0:2])
                ly = listToInt16(data[2:4])
                rx = listToInt16(data[4:6])
                ry = listToInt16(data[6:8])
                self.widget_handle.progress_scale_LX.setValue(lx)
                self.widget_handle.progress_scale_LY.setValue(ly)
                self.widget_handle.progress_scale_RX.setValue(rx)
                self.widget_handle.progress_scale_RY.setValue(ry)
                self.widget_handle.line_scale_LX.setText(str(lx))
                self.widget_handle.line_scale_LY.setText(str(ly))
                self.widget_handle.line_scale_RX.setText(str(rx))
                self.widget_handle.line_scale_RY.setText(str(ry))
                # print("lx: ",lx,"ly: ",ly,"rx: ",rx,"ry: ",ry)
                if self.flag_calibrate_scale:
                    lx_min = int(self.widget_handle.line_scale_LXmin.text())
                    lx_max = int(self.widget_handle.line_scale_LXmax.text())
                    ly_min = int(self.widget_handle.line_scale_LYmin.text())
                    ly_max = int(self.widget_handle.line_scale_LYmax.text())
                    rx_min = int(self.widget_handle.line_scale_RXmin.text())
                    rx_max = int(self.widget_handle.line_scale_RXmax.text())
                    ry_min = int(self.widget_handle.line_scale_RYmin.text())
                    ry_max = int(self.widget_handle.line_scale_RYmax.text())

                    if lx < lx_min:
                        self.widget_handle.line_scale_LXmin.setText(str(lx))
                    if lx > lx_max:
                        self.widget_handle.line_scale_LXmax.setText(str(lx))
                    if ly < ly_min:
                        self.widget_handle.line_scale_LYmin.setText(str(ly))
                    if ly > ly_max:
                        self.widget_handle.line_scale_LYmax.setText(str(ly))
                    if rx < rx_min:
                        self.widget_handle.line_scale_RXmin.setText(str(rx))
                    if rx > rx_max:
                        self.widget_handle.line_scale_RXmax.setText(str(rx))
                    if ry < ry_min:
                        self.widget_handle.line_scale_RYmin.setText(str(ry))
                    if ry > ry_max:
                        self.widget_handle.line_scale_RYmax.setText(str(ry))

            elif ((data[0] == 0x80) & (data[1] == 0x02)):  # 手柄采样数据
                data = data[2:]
                lx_min = listToInt16(data[0:2])
                ly_min = listToInt16(data[2:4])
                rx_min = listToInt16(data[4:6])
                ry_min = listToInt16(data[6:8])
                lx_max = listToInt16(data[8:10])
                ly_max = listToInt16(data[10:12])
                rx_max = listToInt16(data[12:14])
                ry_max = listToInt16(data[14:16])
                # self.custom_rect1.setColorRange(lx_min,lx_max)
                # print(lx_min,ly_min)
                # print(adc1,adc2,adc3,adc4)
            else:
                a = HexStrAddSpace(data.hex())
                self.fuc_log(a)
        elif (self.data_from_board == 0x4401):  # 清洗机器人驱动板
            if ((data[0] == 0x42) & (data[1] == 0x00)):  # 更新扩展输入口状态
                self.widget_clean_driver.check_driver_input_1.setChecked(True if ((data[2] & 0x80) > 0) else False)
                self.widget_clean_driver.check_driver_input_2.setChecked(True if ((data[2] & 0x40) > 0) else False)
                self.widget_clean_driver.check_driver_input_3.setChecked(True if ((data[2] & 0x20) > 0) else False)
                self.widget_clean_driver.check_driver_input_4.setChecked(True if ((data[2] & 0x10) > 0) else False)
                self.widget_clean_driver.check_driver_input_5.setChecked(True if ((data[2] & 0x08) > 0) else False)
                self.widget_clean_driver.check_driver_input_6.setChecked(True if ((data[2] & 0x04) > 0) else False)
                self.widget_clean_driver.check_driver_input_7.setChecked(True if ((data[2] & 0x02) > 0) else False)
                self.widget_clean_driver.check_driver_input_8.setChecked(True if ((data[2] & 0x01) > 0) else False)
                self.widget_clean_driver.check_driver_input_9.setChecked(True if ((data[3] & 0x80) > 0) else False)
                self.widget_clean_driver.check_driver_input_10.setChecked(True if ((data[3] & 0x40) > 0) else False)
                self.widget_clean_driver.check_driver_input_11.setChecked(True if ((data[3] & 0x20) > 0) else False)
                self.widget_clean_driver.check_driver_input_12.setChecked(True if ((data[3] & 0x10) > 0) else False)
                adc1 = listToInt16(data[4:6])
                adc2 = listToInt16(data[6:8])
                self.widget_clean_driver.lineEdit_AInput_1.setText(str(adc1))
                self.widget_clean_driver.lineEdit_AInput_2.setText(str(adc2))
        elif (self.data_from_board == 0x434C):  # 清洗机器人M核控制板
            if ((data[0] == 0x40) & (data[1] == 0x60)):  # 差速轮速度
                data = data[2:]
                currentLineSpeed = listToFloat(data[0:4])
                currentRadSpeed = listToFloat(data[4:8])
                self.widget_clean.input_curentLineSpeed.setText(str(currentLineSpeed))
                self.widget_clean.input_curentRadSpeed.setText(str(currentRadSpeed))
            if ((data[0] == 0x40) & (data[1] == 0x70)):  # 电量、水量、传感器状态
                print('收到4070')
                # 电量百分比 %
                self.widget_clean.input_battery_energy.setText(str(data[2]) + '%')
                # 电池电压
                self.widget_clean.input_battery_voltage.setText(str((data[3] << 8 | data[4]) / 1000))
                # 污水水位
                self.widget_clean.checkBox_dirtyWater1.setChecked(True if ((data[5] & 0x80) > 0) else False)
                self.widget_clean.checkBox_dirtyWater2.setChecked(True if ((data[5] & 0x40) > 0) else False)
                self.widget_clean.checkBox_dirtyWater3.setChecked(True if ((data[5] & 0x20) > 0) else False)
                self.widget_clean.checkBox_dirtyWater4.setChecked(True if ((data[5] & 0x10) > 0) else False)
                self.widget_clean.checkBox_dirtyWater5.setChecked(True if ((data[5] & 0x08) > 0) else False)
                # 清水水位
                self.widget_clean.checkBox_cleanWater1.setChecked(True if ((data[6] & 0x80) > 0) else False)
                self.widget_clean.checkBox_cleanWater2.setChecked(True if ((data[6] & 0x40) > 0) else False)
                self.widget_clean.checkBox_cleanWater3.setChecked(True if ((data[6] & 0x20) > 0) else False)
                self.widget_clean.checkBox_cleanWater4.setChecked(True if ((data[6] & 0x10) > 0) else False)
                self.widget_clean.checkBox_cleanWater5.setChecked(True if ((data[6] & 0x08) > 0) else False)
                # 推杆位置
                self.widget_clean.checkBox_scrub_origin.setChecked(True if ((data[7] & 0x80) > 0) else False)
                self.widget_clean.checkBox_scrub_end.setChecked(True if ((data[7] & 0x40) > 0) else False)
                self.widget_clean.checkBox_scraper_origin.setChecked(True if ((data[7] & 0x20) > 0) else False)
                self.widget_clean.checkBox_scraper_end.setChecked(True if ((data[7] & 0x10) > 0) else False)
                # 超声波避障信息
                self.widget_clean.checkBox_ultrasonic1.setChecked(True if ((data[8] & 0x80) > 0) else False)
                self.widget_clean.checkBox_ultrasonic2.setChecked(True if ((data[8] & 0x40) > 0) else False)
                self.widget_clean.checkBox_ultrasonic3.setChecked(True if ((data[8] & 0x20) > 0) else False)
                self.widget_clean.checkBox_ultrasonic4.setChecked(True if ((data[8] & 0x10) > 0) else False)
                self.widget_clean.checkBox_ultrasonic5.setChecked(True if ((data[8] & 0x08) > 0) else False)
                self.widget_clean.checkBox_ultrasonic6.setChecked(True if ((data[8] & 0x04) > 0) else False)
                self.widget_clean.checkBox_ultrasonic7.setChecked(True if ((data[8] & 0x02) > 0) else False)
                self.widget_clean.checkBox_ultrasonic8.setChecked(True if ((data[8] & 0x01) > 0) else False)
                # 前扫描仪信息
                self.widget_clean.checkBox_radarF1.setChecked(True if ((data[9] & 0x80) > 0) else False)
                self.widget_clean.checkBox_radarF2.setChecked(True if ((data[9] & 0x40) > 0) else False)
                self.widget_clean.checkBox_radarF3.setChecked(True if ((data[9] & 0x20) > 0) else False)

                # 后扫描仪信息
                self.widget_clean.checkBox_radarB1.setChecked(True if ((data[9] & 0x10) > 0) else False)
                self.widget_clean.checkBox_radarB2.setChecked(True if ((data[9] & 0x08) > 0) else False)
                self.widget_clean.checkBox_radarB3.setChecked(True if ((data[9] & 0x04) > 0) else False)

                # 按钮及反馈信息
                self.widget_clean.checkBox_emergency1.setChecked(True if ((data[10] & 0x80) > 0) else False)
                self.widget_clean.checkBox_emergency2.setChecked(True if ((data[10] & 0x40) > 0) else False)
                self.widget_clean.checkBox_reset.setChecked(True if ((data[10] & 0x20) > 0) else False)
                self.widget_clean.checkBox_brake.setChecked(True if ((data[10] & 0x10) > 0) else False)
                self.widget_clean.checkBox_foward.setChecked(True if ((data[10] & 0x08) > 0) else False)
                self.widget_clean.checkBox_backward.setChecked(True if ((data[10] & 0x04) > 0) else False)
                self.widget_clean.checkBox_turnLeft.setChecked(True if ((data[10] & 0x02) > 0) else False)
                self.widget_clean.checkBox_turnRight.setChecked(True if ((data[10] & 0x01) > 0) else False)
                self.widget_clean.checkBox_power_driver.setChecked(True if ((data[7] & 0x08) > 0) else False)
                self.widget_clean.checkBox_charge_feedback.setChecked(True if ((data[7] & 0x04) > 0) else False)
            if ((data[0] == 0x40) & (data[1] == 0x71)):  # 超声波距离信息
                data = data[2:]
                ultra1 = data[1] << 8 | data[0]
                ultra2 = data[3] << 8 | data[2]
                ultra3 = data[5] << 8 | data[4]
                ultra4 = data[7] << 8 | data[6]
                self.widget_clean.lineEdit_ultra_1.setText(str(ultra1))
                self.widget_clean.lineEdit_ultra_2.setText(str(ultra2))
                self.widget_clean.lineEdit_ultra_3.setText(str(ultra3))
                self.widget_clean.lineEdit_ultra_4.setText(str(ultra4))
                # print(ultra1, ultra2, ultra3, ultra4)
        elif (self.data_from_board == 0x434F):  # 清洗机工作站M核控制板
            if ((data[0] == 0x40) & (data[1] == 0x70)):
                self.widget_clean.checkBox_station_emergency1.setChecked(True if ((data[2] & 0x80) > 0) else False)
                self.widget_clean.checkBox_station_emergency2.setChecked(True if ((data[2] & 0x40) > 0) else False)
                self.widget_clean.checkBox_station_clean.setChecked(True if ((data[2] & 0x20) > 0) else False)
                self.widget_clean.checkBox_station_auto.setChecked(True if ((data[2] & 0x10) > 0) else False)
                self.widget_clean.checkBox_station_manual.setChecked(True if ((data[2] & 0x08) > 0) else False)
                self.widget_clean.checkBox_station_extend.setChecked(True if ((data[2] & 0x04) > 0) else False)
                self.widget_clean.checkBox_station_retract.setChecked(True if ((data[2] & 0x02) > 0) else False)
                self.widget_clean.checkBox_station_extendIn.setChecked(True if ((data[2] & 0x01) > 0) else False)
                self.widget_clean.checkBox_station_retractIn.setChecked(True if ((data[3] & 0x80) > 0) else False)
                self.widget_clean.checkBox_station_cleanLevel.setChecked(True if ((data[3] & 0x40) > 0) else False)
                self.widget_clean.checkBox_station_sewageLevel.setChecked(True if ((data[3] & 0x20) > 0) else False)
                self.widget_clean.checkBox_station_agvIn.setChecked(True if ((data[3] & 0x10) > 0) else False)
                self.widget_clean.checkBox_station_charge.setChecked(True if ((data[3] & 0x08) > 0) else False)
                self.widget_clean.checkBox_station_fault.setChecked(True if ((data[3] & 0x04) > 0) else False)
        elif (self.data_from_board == 0x4354):  # CTU上装机构M核控制板
            if ((data[0] == 0x40) & (data[1] == 0x70)):
                self.widget_ctu_up.checkBox_limit_up.setChecked(True if ((data[2] & 0x80) > 0) else False)
                self.widget_ctu_up.checkBox_limit_down.setChecked(True if ((data[2] & 0x40) > 0) else False)
                self.widget_ctu_up.checkBox_limit_left.setChecked(True if ((data[2] & 0x20) > 0) else False)
                self.widget_ctu_up.checkBox_limit_right.setChecked(True if ((data[2] & 0x10) > 0) else False)
                self.widget_ctu_up.checkBox_limit_extend.setChecked(True if ((data[2] & 0x08) > 0) else False)
                self.widget_ctu_up.checkBox_limit_retract.setChecked(True if ((data[2] & 0x04) > 0) else False)
                self.widget_ctu_up.checkBox_limit_clampL.setChecked(True if ((data[2] & 0x02) > 0) else False)
                self.widget_ctu_up.checkBox_limit_clampR.setChecked(True if ((data[2] & 0x01) > 0) else False)
                self.widget_ctu_up.checkBox_limit_chainL.setChecked(True if ((data[3] & 0x80) > 0) else False)
                self.widget_ctu_up.checkBox_limit_chainR.setChecked(True if ((data[3] & 0x40) > 0) else False)
                self.widget_ctu_up.checkBox_limit_offsetL.setChecked(True if ((data[3] & 0x20) > 0) else False)
                self.widget_ctu_up.checkBox_limit_offsetR.setChecked(True if ((data[3] & 0x10) > 0) else False)
            if ((data[0] == 0x40) & (data[1] == 0x71)):  # 各电机位置上报
                data = data[2:]
                if (len(data) >= 4):  # 升降电机
                    motor2_postion = listToInt32_little(data[0:4])
                    self.widget_ctu_up.input_motor2_position.setText(str(motor2_postion))
                if (len(data) >= 8):
                    motor3_postion = listToInt32_little(data[4:8])
                    self.widget_ctu_up.input_motor3_position.setText(str(motor3_postion))
                if (len(data) >= 12):
                    motor4_postion = listToInt32_little(data[8:12])
                    self.widget_ctu_up.input_motor4_position.setText(str(motor4_postion))
                if (len(data) >= 16):
                    motor5_postion = listToInt32_little(data[12:16])
                    self.widget_ctu_up.input_motor5_position.setText(str(motor5_postion))
                if (len(data) >= 16):
                    lever_L_Pos = listToInt16(data[16:18])
                    self.widget_ctu_up.lineEdit_lever_L_Pos.setText(str(lever_L_Pos))
                if (len(data) >= 18):
                    lever_R_Pos = listToInt16(data[18:20])
                    self.widget_ctu_up.lineEdit_lever_R_Pos.setText(str(lever_R_Pos))
            if ((data[0] == 0x40) & (data[1] == 0x72)):  # 各电机速度上报
                data = data[2:]
                if (len(data) >= 4):  # 升降电机
                    motor2_speed = listToInt32_little(data[0:4])
                    self.widget_ctu_up.input_motor2_speed.setText(str(motor2_speed))
                if (len(data) >= 8):
                    motor3_speed = listToInt32_little(data[4:8])
                    self.widget_ctu_up.input_motor3_speed.setText(str(motor3_speed))
                if (len(data) >= 12):
                    motor4_speed = listToInt32_little(data[8:12])
                    self.widget_ctu_up.input_motor4_speed.setText(str(motor4_speed))
                if (len(data) >= 16):
                    motor5_speed = listToInt32_little(data[12:16])
                    self.widget_ctu_up.input_motor5_speed.setText(str(motor5_speed))
            if ((data[0] == 0x50) & (data[1] == 0x03)):  # 相机偏移量、错误状态
                data = data[2:]
                bar_code = (data[0:19]).decode('gbk')
                offset_UD = data[19]
                offset_LR = data[20]
                state_err = data[21]
                if offset_UD > 0x7F:
                    offset_UD = offset_UD - 0x100
                if offset_LR > 0x7F:
                    offset_LR = offset_LR - 0x100
                self.widget_ctu_up.lineEdit_camera_offset_LR.setText(str(offset_LR))
                self.widget_ctu_up.lineEdit_camera_offset_UD.setText(str(offset_UD))
                self.widget_ctu_up.lineEdit_bar_code.setText(bar_code)
                self.widget_ctu_up.lineEdit_state_err.setText(hex(state_err))
                # print(data[19], data[20], data[21], data[22])
        elif (self.data_from_board == 0x4357):  # CTU下装机构M核控制板
            if ((data[0] == 0x40) & (data[1] == 0x70)):
                # 输入口状态
                self.widget_ctu_up.checkBox_in_emergency.setChecked(True if ((data[2] & 0x80) > 0) else False)
                self.widget_ctu_up.checkBox_in_collision.setChecked(True if ((data[2] & 0x40) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarF1.setChecked(True if ((data[2] & 0x20) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarF2.setChecked(True if ((data[2] & 0x10) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarF3.setChecked(True if ((data[2] & 0x08) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarB1.setChecked(True if ((data[2] & 0x04) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarB2.setChecked(True if ((data[2] & 0x02) > 0) else False)
                self.widget_ctu_up.checkBox_in_radarB3.setChecked(True if ((data[2] & 0x01) > 0) else False)
                self.widget_ctu_up.checkBox_in_mannual.setChecked(True if ((data[3] & 0x80) > 0) else False)
                self.widget_ctu_up.checkBox_in_reset.setChecked(True if ((data[3] & 0x40) > 0) else False)
                self.widget_ctu_up.checkBox_in_brake.setChecked(True if ((data[3] & 0x20) > 0) else False)
                self.widget_ctu_up.checkBox_in_walk.setChecked(True if ((data[3] & 0x10) > 0) else False)
                self.widget_ctu_up.checkBox_in_lift.setChecked(True if ((data[3] & 0x08) > 0) else False)

                # 电量
                voltage = (data[4] * 256 + data[5]) / 1000
                energy = (data[6] * 256 + data[7]) / 1000
                self.widget_ctu_up.input_battery_voltage.setText(str(voltage))
                self.widget_ctu_up.input_battery_energy.setText(str(energy))
        elif (self.data_from_board == 0x5354):  # CTU下装机构M核控制板
            if ((data[0] == 0x40) & (data[1] == 0x60)):
                data = data[2:]
                # 差速轮速度
                currentLineSpeed = listToFloat(data[0:4])
                currentRadSpeed = listToFloat(data[4:8])
                self.input_agv_curentLineSpeed.setText(str(currentLineSpeed))
                self.input_agv_curentRateSpeed.setText(str(currentRadSpeed))

    def server_parse_data(self, data):  # 解析调度系统数据
        Distribution_code = data[12:16]
        if Distribution_code == b'AFLF':  # 验证接收数据
            CMD_STATE = list(data[24:64])
            # for i in range(20):#数据存储大小端交换
            #     CMD_STATE[2*i:2*i+2]=CMD_STATE[2*i:2*i+2][::-1]
            CMD_STATE_note = ["未开始", " 开始 ", "执行中", " 完成 ", " 错误 "]
            CMD_STATE_color = ["grey", " black", "blue", " green", " red"]
            self.flag_doneCMD = True
            for i in range(10):
                cmdNum = CMD_STATE[0 + 4 * i] << 8 | CMD_STATE[1 + 4 * i]  # 返回的状态序号
                cmdState = CMD_STATE[3 + 4 * i]
                # 调度监测界面
                if cmdNum == 0:
                    continue
                state_note = CMD_STATE_note[cmdState] if cmdState < len(CMD_STATE_note) else "Unknown"
                current_time = get_time()
                item_text = f"{current_time} Cmd: {cmdNum}, State: {state_note}"
                # 添加到列表控件
                if self.widget_tcs_watch.listWidget.count() > 20:
                    self.widget_tcs_watch.listWidget.takeItem(0)
                self.widget_tcs_watch.listWidget.addItem(item_text)
                self.widget_tcs_watch.listWidget.scrollToBottom()

                # 调度模拟界面
                widget = self.gridLayout_25.itemAtPosition(i, 0).widget()
                widget.setText(str(cmdNum))
                widget = self.gridLayout_25.itemAtPosition(i, 1).widget()
                widget.setText(CMD_STATE_note[cmdState])
                widget.setStyleSheet("color:" + CMD_STATE_color[cmdState])  # 根据状态号设置状态颜色
                if cmdNum != 0 and cmdState != 3:  # 全部有效指令已完成
                    self.flag_doneCMD = False

            if self.flag_doneCMD and self.checkBox_cmdCycle.isChecked():
                print("指令全部完成")
                self.count_cmdDone += 1
                if self.count_cmdDone >= 2:
                    self.count_cmdDone = 0
                    widget = self.layout_cmd.itemAt(0).widget()
                    if widget.isChecked():
                        widget.setChecked(False)
                    else:
                        widget.setChecked(True)

                    if widget.isChecked():
                        widget.setChecked(False)
                    else:
                        widget.setChecked(True)

                    self.fuc_sendCmd_TCS()
                # time.sleep(1)

            posX = listToInt32(data[64:68])  # 当前位置X
            posY = listToInt32(data[68:72])  # 当前位置Y
            angle = listToInt32(data[72:76])  # 当前角度
            speed = listToInt32(data[76:80])  # 当前线速度
            nav_mode = 0  # 导航模式
            lift = 0  # 顶升
            tab_degree = 0  # 转盘角度
            reserve_degree = 0  # 预留角度
            RFID_last = listToUint16(data[88:90])  # 上一个RFID
            # tag_number=listToInt32(data[91:93]) #当前RFID
            # RFID=listToUint16(data[90:92]) #当前RFID
            RFID = data[90] << 8 | data[91]  # 当前RFID
            speed_x = 0  # X方向速度
            speed_y = 0  # X方向速度
            rateSpeed = listToInt32(data[100:104])  # 当前角速度
            Errors = []  # 错误位
            Warn = []  # 警告 位
            Status = []  # 状态位
            battery = listToInt32(data[124:126])  # 当前电池电量百分比
            capacity = 0  # 当前电容电量百分比
            odometer = 0  # 里程计
            BAT_health = 0  # 电池健康度
            Reserved = 0  # 预留
            # 调度监测界面
            self.widget_tcs_watch.input_fb_posX.setText(str(posX))
            self.widget_tcs_watch.input_fb_posY.setText(str(posY))
            self.widget_tcs_watch.input_fb_angle.setText(str(angle))
            self.widget_tcs_watch.input_fb_speed.setText(str(speed))
            self.widget_tcs_watch.input_fb_battery.setText(str(battery) + '%')
            self.widget_tcs_watch.input_fb_rateSpeed.setText(str(rateSpeed))
            self.widget_tcs_watch.input_fb_Status.setText(str(Status))
            self.widget_tcs_watch.input_fb_Errors.setText(str(Errors))
            self.widget_tcs_watch.input_fb_RFID.setText(str(RFID))

            # 调度模拟界面
            self.input_fb_posX.setText(str(posX))
            self.input_fb_posY.setText(str(posY))
            self.input_fb_angle.setText(str(angle))
            self.input_fb_speed.setText(str(speed))
            self.input_fb_battery.setText(str(battery) + '%')
            self.input_fb_rateSpeed.setText(str(rateSpeed))
            self.input_fb_Status.setText(str(Status))
            self.input_fb_Errors.setText(str(Errors))
            self.input_fb_RFID.setText(str(RFID))

            # 更新运动轨迹
            agv_currentPosX = posX / 1000
            agv_currentPosY = posY / 1000
            # self.thread_position.update(agv_currentPosX, agv_currentPosY)  # 更新运动轨迹

    def server_parse_data1(self, data):  # 解析调度系统数据
        # print(len(data))
        Distribution_code = data[12:16]
        if Distribution_code == b'AFLF':  # 验证接收数据
            CMD_STATE = list(data[24:64])
            # for i in range(20):#数据存储大小端交换
            #     CMD_STATE[2*i:2*i+2]=CMD_STATE[2*i:2*i+2][::-1]
            CMD_STATE_note = ["未开始", " 开始 ", "执行中", " 完成 ", " 错误 "]
            CMD_STATE_color = ["grey", " black", "blue", " green", " red"]
            self.flag_doneCMD = True
            for i in range(10):
                # cmdNum=CMD_STATE[0+4*i]*16+CMD_STATE[1+4*i]#data[0]data[1]为返回的状态序号
                # data[0]data[1]为返回的状态序号
                cmdNum = CMD_STATE[0 + 4 * i] << 8 | CMD_STATE[1 + 4 * i]
                cmdState = CMD_STATE[3 + 4 * i]
                widget = self.gridLayout_25.itemAtPosition(i, 0).widget()
                widget.setText(str(cmdNum))
                widget = self.gridLayout_25.itemAtPosition(i, 1).widget()
                widget.setText(CMD_STATE_note[cmdState])
                widget.setStyleSheet("color:" + CMD_STATE_color[cmdState])  # 根据状态号设置状态颜色
                if cmdNum != 0 and cmdState != 3:  # 全部有效指令已完成
                    self.flag_doneCMD = False
                # for j in range(self.num_cmd):
                #     widget = self.layout_cmd.itemAt(11+(0)*self.num_cmd+j).widget()
                #     if widget.isChecked():#判断复选框是否被选中
                #         widget = self.layout_cmd.itemAt(11+(1)*self.num_cmd+j).widget()
                #         if widget.text()==str(cmdNum):#判断命令号相同,默认初始反馈的10条命令命令号均为0时，下发命令号为0的命令，状态会被其他命令号为0的状态覆盖
                #             widget = self.layout_cmd.itemAt(11+(10)*self.num_cmd+j).widget()
                #             widget.setText(CMD_STATE_note[cmdState])#data[3]为返回的状态命令位，根据状态号设置状态名字
                #             widget.setStyleSheet("color:"+CMD_STATE_color[cmdState])#根据状态号设置状态颜色
                # if cmdState==1:
                # if i==9:
                #     print("cmdState:",cmdState)
                #     if cmdState==3:
                #         print("执行完成")
                #         if self.checkBox_cmdCycle.isChecked():
                #             print("循环下发")

            if self.flag_doneCMD and self.checkBox_cmdCycle.isChecked():
                print("指令全部完成")
                self.count_cmdDone += 1
                if self.count_cmdDone >= 2:
                    self.count_cmdDone = 0
                    widget = self.layout_cmd.itemAt(0).widget()
                    if widget.isChecked():
                        widget.setChecked(False)
                    else:
                        widget.setChecked(True)

                    if widget.isChecked():
                        widget.setChecked(False)
                    else:
                        widget.setChecked(True)

                    self.fuc_sendCmd_TCS()
                # time.sleep(1)

            posX = listToInt32(data[64:68])  # 当前位置X
            posY = listToInt32(data[68:72])  # 当前位置Y
            angle = listToInt32(data[72:76])  # 当前角度
            speed = listToInt32(data[76:80])  # 当前线速度
            nav_mode = 0  # 导航模式
            lift = 0  # 顶升
            tab_degree = 0  # 转盘角度
            reserve_degree = 0  # 预留角度
            RFID_last = listToUint16(data[88:90])  # 上一个RFID
            # tag_number=listToInt32(data[91:93]) #当前RFID
            # RFID=listToUint16(data[90:92]) #当前RFID
            RFID = data[90] << 8 | data[91]  # 当前RFID
            speed_x = 0  # X方向速度
            speed_y = 0  # X方向速度
            rateSpeed = listToInt32(data[100:104])  # 当前角速度
            Errors = []  # 错误位
            Warn = []  # 警告 位
            Status = []  # 状态位
            battery = listToInt32(data[124:126])  # 当前电池电量百分比
            capacity = 0  # 当前电容电量百分比
            odometer = 0  # 里程计
            BAT_health = 0  # 电池健康度
            Reserved = 0  # 预留
            self.input_fb_posX.setText(str(posX))
            self.input_fb_posY.setText(str(posY))
            self.input_fb_angle.setText(str(angle))
            self.input_fb_speed.setText(str(speed))
            self.input_fb_battery.setText(str(battery) + '%')
            self.input_fb_rateSpeed.setText(str(rateSpeed))
            self.input_fb_Status.setText(str(Status))
            self.input_fb_Errors.setText(str(Errors))
            self.input_fb_RFID.setText(str(RFID))

            # 更新运动轨迹
            agv_currentPosX = posX / 1000
            agv_currentPosY = posY / 1000
            self.thread_position.update(agv_currentPosX, agv_currentPosY)  # 更新运动轨迹
            # if((self.last_agv_currentPosX!=agv_currentPosX)|(self.last_agv_currentPosY!=agv_currentPosY)):
            #     self.paint.dotdata.append({'pos': (agv_currentPosX, agv_currentPosY),'brush': pg.mkBrush('g'),'pen':pg.mkPen(color='g')})  # 设置每个点的颜色
            #     self.paint.scatter.setData(self.paint.dotdata)
            # self.last_agv_currentPosX=agv_currentPosX
            # self.last_agv_currentPosY=agv_currentPosY
            # print(data[64],data[65],data[66],data[67])
        # flag_cmd_cycle=1
        # for i in range(self.cmd_count):
        #     if CMD_STATE[2+4*i]!=4:
        #         flag_cmd_cycle=0
        # if (flag_cmd_cycle==1)&(self.check_cmd_cycle.isChecked()):
        #     self.fuc_sendCmd_all()
        # AGV_STATE=data[64:96]
        # # self.signal.print.emit('收到数据: %s' % AGV_STATE)
        # self.input_fb_posX.setText(str(int.from_bytes(AGV_STATE[:4], byteorder='little',signed=True)/10))
        # self.input_fb_posY.setText(str(int.from_bytes(AGV_STATE[4:8], byteorder='little',signed=True)/10))
        # self.input_fb_angle.setText(str(int.from_bytes(AGV_STATE[8:10], byteorder='little',signed=True)/10))
        # self.input_fb_speed.setText(str(int.from_bytes(AGV_STATE[12:16], byteorder='little',signed=True)/10))
        # self.input_fb_angle.setText(str(struct.unpack('<H', AGV_STATE[8:10])[0]))

    # endregion
    # region # AGV运动控制
    def fuc_agv_stop(self):  # 停止
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = 0
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)
        time.sleep(0.1)
        self.fuc_stop_RU()  # 停止转盘和顶升

    def fuc_agv_moveForward(self):  # 前进
        if self.protocol == 'My':
            cmd_id = [0x40, 0x60]
            lineSpeed = float(self.input_agv_lineSpeed.text())
            rateSpeed = 0
            cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
            self.fuc_serial_send(cmd_id, cmd_data)
            self.add_keyDot(lineSpeed, rateSpeed)
        elif self.protocol == 'TCS':
            self.cmdNum += 1
            self.fuc_sendUiCmd_TCS(ID=self.cmdNum, name="GP", p1=300)

    def fuc_agv_moveBack(self):  # 后退
        cmd_id = [0x40, 0x60]
        lineSpeed = -float(self.input_agv_lineSpeed.text())
        rateSpeed = 0
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)

    def fuc_agv_rotateCW(self):  # 正转
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = -float(self.input_agv_rateSpeed.text())
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)

    def fuc_agv_rotateCCW(self):  # 反转
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = float(self.input_agv_rateSpeed.text())
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)

    def fuc_agv_run(self):  # 曲线运行
        cmd_id = [0x40, 0x60]
        lineSpeed = float(self.input_agv_lineSpeed.text())
        rateSpeed = float(self.input_agv_rateSpeed.text())
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)

    def fuc_agv_runBack(self):  # 反向曲线运行
        cmd_id = [0x40, 0x60]
        lineSpeed = -float(self.input_agv_lineSpeed.text())
        rateSpeed = -float(self.input_agv_rateSpeed.text())
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)
        self.add_keyDot(lineSpeed, rateSpeed)

    def fuc_agv_toAngle(self):  # 旋转到指定角度
        cmd_id = [0x40, 0x32]
        angle = int(float(self.input_agv_targetAngle.text()) * 10)
        cmd_data = [angle >> 8 & 0xff, angle & 0xff]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_agv_toDistance(self):  # 运行指定距离
        cmd_id = [0x40, 0x33]
        distance = round(float(self.input_agv_targetDistance.text()), 2)
        cmd_data = floatToList(distance)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_agv_toPos(self):  # 运行到指定点
        cmd_id = [0x40, 0x34]
        agv_targetPosX = floatToList(round(float(self.input_agv_targetPosX.text()), 5))
        agv_targetPosY = floatToList(round(float(self.input_agv_targetPosY.text()), 5))
        cmd_data = agv_targetPosX + agv_targetPosY
        self.fuc_serial_send(cmd_id, cmd_data)

        self.paint.dotdata.append({
            'pos': (round(float(self.input_agv_targetPosX.text()), 2), round(float(self.input_agv_targetPosY.text()), 2)),
            'brush': pg.mkBrush('r')
        })
        self.paint.scatter.setData(self.paint.dotdata)

    def fuc_agv_resetPos(self):  # 位置坐标复位
        cmd_id = [0x40, 0x35]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_agv_resetAngle(self):  # 角度复位
        # cmd_id=[0x40,0x21]
        # cmd_data=[0x00,0x00]
        # self.fuc_serial_send(cmd_id,cmd_data)
        cmd_id = [0x40, 0x22]
        # angle=int(float(self.input_angleRectify.text())*10)
        angle = 0
        cmd_data = [angle >> 8, angle & 0xff]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_obstacle_ignore(self):  # 配置雷达避障
        cmd_id = [0x40, 0x55]
        cmd_data = [0x00, 0x00]
        if (self.check_obstacle_ignore.isChecked()):
            cmd_data = [0x01, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_watch_agvState(self):  # AGV状态监测开启/关闭
        cmd_id = [0x80, 0x02]
        cmd_data = [0x01, 0x00]
        if (self.check_watch_agvState.isChecked()):
            cmd_data = [0x01, 0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_enMotor(self):
        en = self.checkBox_enMotor.isChecked()
        cmd_id = [0x40, 0x11]
        cmd_data = [en, 0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motor_protect(self):
        cmd_id = [0x40, 0x12]
        cmd_data = [0xff, 0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # 转盘及升降机构控制
    def fuc_stop_RU(self):
        cmd_id = [0x40, 0x70]
        cmd_data = [0xff, 0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_rotator_CW(self):
        cmd_id = [0x40, 0x71]
        speed = abs(int(self.input_motorT_speed_T.text()))
        if speed > 3000:
            speed = 3000
            self.input_motorT_speed_T.setText('3000')
        cmd_data = int32Tolist(speed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_rotator_CCW(self):
        cmd_id = [0x40, 0x71]
        speed = -abs(int(self.input_motorT_speed_T.text()))
        if speed < -3000:
            speed = -3000
            self.input_motorT_speed_T.setText('-3000')
        cmd_data = int32Tolist(speed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_liftor_up(self):
        cmd_id = [0x40, 0x80]
        speed = abs(int(self.input_motorU_speed_T.text()))
        if speed > 3000:
            speed = 3000
            self.input_motorU_speed_T.setText('3000')
        cmd_data = int32Tolist(-speed)  # 反转
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_liftor_down(self):
        cmd_id = [0x40, 0x80]
        speed = -abs(int(self.input_motorU_speed_T.text()))
        if speed < -3000:
            speed = -3000
            self.input_motorU_speed_T.setText('-3000')
        cmd_data = int32Tolist(-speed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorT_mode(self):
        cmd_id = [0x40, 0x12]
        mode = int(self.com_motorT_mode.currentText().split(':')[0])
        if mode < 0:
            mode = 0x100 + mode
        cmd_data = [0x05, mode]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorU_mode(self):
        cmd_id = [0x40, 0x12]
        mode = int(self.com_motorU_mode.currentText().split(':')[0])
        if mode < 0:
            mode = 0x100 + mode
        cmd_data = [0x04, mode]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motor_findOrigin(self, ID):
        cmd_id = [0x40, 0x72]
        cmd_data = [ID]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorT_setOrigin(self):  # 原点标定
        cmd_id = [0x40, 0x15]
        cmd_data = [0x05]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorU_setOrigin(self):
        cmd_id = [0x40, 0x15]
        cmd_data = [0x04]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorT_position_T(self):
        cmd_id = [0x40, 0x13]
        speed = int(self.input_motorT_speed_T.text())
        if speed > 3000:
            speed = 3000
            self.input_motorT_speed_T.setText('3000')
        elif speed < -3000:
            speed = -3000
            self.input_motorT_speed_T.setText('-3000')
        cmd_data = [0x05] + int32Tolist(int(self.input_motorT_position_T.text())) + int32Tolist(speed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorU_position_T(self):
        cmd_id = [0x40, 0x13]
        speed = int(self.input_motorU_speed_T.text())
        if speed > 500:
            speed = 500
            self.input_motorU_speed_T.setText('500')
        elif speed < -500:
            speed = -500
            self.input_motorU_speed_T.setText('-500')
        cmd_data = [0x04] + int32Tolist(int(self.input_motorU_position_T.text())) + int32Tolist(speed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorT_angle_T(self):
        cmd_id = [0x40, 0x17]
        speed = int(self.input_motorT_speed_T.text())
        if speed > 3000:
            speed = 3000
            self.input_motorT_speed_T.setText('3000')
        elif speed < -3000:
            speed = -3000
            self.input_motorT_speed_T.setText('-3000')
        cmd_data = int32Tolist(int(self.input_motorT_angle_T.text())) + int32Tolist(speed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motorU_height_T(self):
        cmd_id = [0x40, 0x18]
        speed = int(self.input_motorU_speed_T.text())
        if speed > 3000:
            speed = 3000
            self.input_motorU_speed_T.setText('500')
        elif speed < -3000:
            speed = -3000
            self.input_motorU_speed_T.setText('-500')
        cmd_data = int32Tolist(int(self.input_motorU_height_T.text())) + int32Tolist(speed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_liftor_upDown(self, state):
        cmd_id = [0x40, 0x19]
        if state:
            cmd_data = [0x01]
        else:
            cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motor_disconnect(self):
        cmd_id = [0x40, 0x14]
        cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_motor_communicate(self):
        cmd_id = [0x40, 0x14]
        if self.check_motor_communicate.isChecked():
            cmd_data = [0x01]
        else:
            cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_rotator_RR(self):  # 转盘同AGV同步旋转
        cmd_id = [0x40, 0x16]
        if self.check_rotator_RR.isChecked():
            cmd_data = [0x01]
        else:
            cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_tmp_test(self):
        cmd_id = [0xF0, 0x00]
        cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # 运动轨迹(地图)
    def toggle_widget_map(self, checked):
        self.widget_map.show()

    def fuc_clear_points(self):
        self.paint.dotdata = []
        self.thread_position.clear()
        # self.paint.scatter.setData(self.paint.dotdata)
        # self.keyDots=[]

    def fuc_saveTrajectory(self, text):
        print(self.paint.dotdata)
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/trajectory")
        with open(filename, "w", encoding='utf-8') as f:
            json.dump(self.paint.dotdata, f)

    def fuc_loadTrajectory(self, text):
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/trajectory")
        if filename:
            with open(filename, "r", encoding='utf-8') as f:
                content = f.read()
            self.paint.dotdata = json.loads(content)
            self.paint.scatter.setData(self.paint.dotdata)
        else:
            self.fuc_log('取消加载')

    def fuc_playTrajectory(self, direction):
        self.direction_playTrajectory = direction
        self.dotdata = []
        self.paint.scatter.setData(self.dotdata)  # 清空点
        self.points_number = len(self.paint.dotdata)  # 轨迹中点的数量
        if direction:
            self.point_count = 0  # 从第0个点开始
        else:
            self.point_count = self.points_number - 1  # 从最后一个点开始
        self.timer9_playPoints.start(300)  # 每300ms打印1个点

    def task_playPoints(self):
        if self.direction_playTrajectory:
            if self.point_count < self.points_number:
                self.dotdata.append(self.paint.dotdata[self.point_count])
                self.paint.scatter.setData(self.dotdata)
                self.point_count += 1
            else:
                self.timer9_playPoints.stop()
        else:
            if self.point_count >= 0:
                self.dotdata.append(self.paint.dotdata[self.point_count])
                self.paint.scatter.setData(self.dotdata)
                self.point_count -= 1
            else:
                self.timer9_playPoints.stop()

    def add_keyDot(self, lineSpeed, rateSpeed):
        PosX = float(self.input_agv_currentPosX.text())
        PosY = float(self.input_agv_currentPosY.text())

        # 新增红色关键点
        # old_value ={'pos': (PosX, PosY)}
        # new_value ={'pos': (PosX, PosY),'brush': 'r','pen':'r','data':[lineSpeed,rateSpeed]}
        # self.paint.dotdata = [new_value if x == old_value else x for x in self.paint.dotdata]
        # self.paint.scatter.setData(self.paint.dotdata)

        # self.keyDots.append(new_value)

    def fuc_save_keyDot(self, text):
        print(self.keyDots)
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/trajectory")
        with open(filename, "w", encoding='utf-8') as f:
            json.dump(self.keyDots, f)

    def fuc_load_keyDot(self, text):
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/trajectory")
        with open(filename, "r", encoding='utf-8') as f:
            content = f.read()
        self.keyDots = json.loads(content)
        self.paint.scatter.setData(self.keyDots)

    # 多点路径规划
    def fuc_pointCycle(self):
        cmd_id = [0x40, 0x52]
        map = int(self.widget_map.com_map.currentText())
        route = int(self.widget_map.com_route.currentText())
        startPoint = int(self.widget_map.input_startPoint.text())
        if (self.widget_map.check_pointCycle.isChecked()):
            cmd_data = [0x01, map, route, startPoint]
        else:
            cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_startPointRecycle(self):
        cmd_id = [0x40, 0x36]
        cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # 轨迹跟踪参数
    def fuc_trace_query(self):
        cmd_id = [0x40, 0x40]
        cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_trace_set(self):
        cmd_id = [0x40, 0x40]
        K_Cross = float(self.widget_standly.lineEdit_K_Cross.text())
        K_LowVelo = float(self.widget_standly.lineEdit_K_LowVelo.text())
        K_trace_angle = float(self.widget_standly.lineEdit_K_trace_angle.text())
        K_cross_angle = float(self.widget_standly.lineEdit_K_cross_angle.text())
        cmd_data = [0x01] + floatToList(K_Cross) + floatToList(K_LowVelo) + \
            floatToList(K_trace_angle) + floatToList(K_cross_angle)
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # AGV参数配置
    def fuc_system_sleep(self):
        cmd_id = [0x00, 0x01]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_system_reset(self):
        cmd_id = [0x10, 0x00]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_software_reset(self):
        cmd_id = [0x10, 0x00]
        cmd_data = [0x03, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_system_restart(self):
        cmd_id = [0x10, 0x01]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_eeprom_init(self):
        cmd_id = [0x10, 0x00]
        cmd_data = [0x01, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_activeChip(self):
        cmd_id = [0x10, 0x00]
        cmd_data = [0x02, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_rtc_init(self):
        cmd_id = [0x10, 0x00]
        # 获取当前日期和时间
        now = datetime.now()
        year = now.year - 2000
        # year = now.year-1999
        month = now.month
        day = now.day
        hour = now.hour
        minute = now.minute
        second = now.second
        msecond = now.microsecond // 1000
        datetime_array = [year, month, day, hour, minute, second, msecond]
        cmd_data = [0x03, year, month, day, hour, minute, second, msecond // 256, msecond % 256]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_save_flashParameter(self):
        cmd_id = [0x10, 0x05]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_read_flashParameter(self):
        cmd_id = [0x10, 0x02]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)
        self.btn_read_flashParameter.setText("读取中……")

    def fuc_set_flashParameter(self):
        cmd_id = [0x10, 0x03]
        cmd_data = []
        for i in range(len(self.agv_parameter)):
            found_widget = self.widget_agvParameter.findChild(QLineEdit, self.agv_parameter[i])  # 根据字符串查找widget中的对象
            if i == 0:  # 第一个参数为版本号，是整数不是小数
                cmd_data += [int(found_widget.text()), 0, 0, 0]
            else:
                if found_widget.text() == '':
                    found_widget.setText('0')
                cmd_data += floatToList(float(found_widget.text()))

        # scale_rateToLine     =floatToList(float(self.input_scale_rateToLine     .text()))
        # agv_default_lineSpeed=floatToList(float(self.input_agv_default_lineSpeed.text()))
        # agv_default_rateSpeed=floatToList(float(self.input_agv_default_rateSpeed.text()))
        # agv_acceleration     =floatToList(float(self.input_agv_acceleration     .text()))
        # agv_deceleration     =floatToList(float(self.input_agv_deceleration     .text()))
        # agv_RateAcceleration =floatToList(float(self.input_agv_RateAcceleration .text()))
        # agv_RateDeceleration =floatToList(float(self.input_agv_RateDeceleration .text()))
        # cmd_data=scale_rateToLine+agv_default_lineSpeed+agv_default_rateSpeed+agv_acceleration+agv_deceleration+agv_RateAcceleration+agv_RateDeceleration+[0x01]
        # self.fuc_serial_send(cmd_id,cmd_data)
        # time.sleep(0.1)
        # imu_integralGain     =floatToList(float(self.input_imu_integralGain     .text()))
        # imu_GyroOffset_Z=floatToList(float(self.input_imu_offsetZ.text()))
        # agv_angleRangeOffset=floatToList(float(self.input_angleRangeOffset.text()))
        # agv_distanceRangeOffset=floatToList(float(self.input_distanceRangeOffset.text()))
        # factor_rectify=floatToList(float(self.input_factor_rectify.text()))
        # rectifyAngle=floatToList(float(self.input_rectifyAngle.text()))
        # cmd_data=imu_integralGain+imu_GyroOffset_Z+agv_angleRangeOffset+agv_distanceRangeOffset+factor_rectify+rectifyAngle+agv_RateDeceleration+[0x02]
        # self.fuc_serial_send(cmd_id,cmd_data)
        # time.sleep(0.1)
        # LW_compensation=floatToList(float(self.input_LW_compensation.text()))
        # RW_compensation=floatToList(float(self.input_RW_compensation.text()))
        # LW_offset=floatToList(float(self.input_LW_offset.text()))
        # RW_offset=floatToList(float(self.input_RW_offset.text()))
        # cmd_data=LW_compensation+RW_compensation+LW_offset+RW_offset+factor_rectify+rectifyAngle+agv_RateDeceleration+[0x03]
        print(len(cmd_data))
        if (cmd_data[0] != 101):
            self.fuc_log("请先读取参数！")
        else:
            self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_save_parameter(self):
        # scale_rateToLine       =self.input_scale_rateToLine     .text()
        # agv_default_lineSpeed  =self.input_agv_default_lineSpeed.text()
        # agv_default_rateSpeed  =self.input_agv_default_rateSpeed.text()
        # agv_acceleration       =self.input_agv_acceleration     .text()
        # agv_deceleration       =self.input_agv_deceleration     .text()
        # agv_RateAcceleration   =self.input_agv_RateAcceleration .text()
        # agv_RateDeceleration   =self.input_agv_RateDeceleration .text()
        # imu_integralGain       =self.input_imu_integralGain     .text()
        # imu_GyroOffset_Z       =self.input_imu_offsetZ           .text()
        # agv_angleRangeOffset   =self.input_angleRangeOffset     .text()
        # agv_distanceRangeOffset=self.input_distanceRangeOffset  .text()
        # factor_rectify         =self.input_factor_rectify       .text()
        # rectifyAngle           =self.input_rectifyAngle         .text()
        # LW_compensation        =self.input_LW_compensation      .text()
        # RW_compensation        =self.input_RW_compensation      .text()
        # LW_offset              =self.input_LW_offset            .text()
        # RW_offset              =self.input_RW_offset            .text()
        # text="scale_rateToLine       :"+scale_rateToLine       +"\n"\
        #     +"agv_default_lineSpeed  :"+agv_default_lineSpeed  +"\n"\
        #     +"agv_default_rateSpeed  :"+agv_default_rateSpeed  +"\n"\
        #     +"agv_acceleration       :"+agv_acceleration       +"\n"\
        #     +"agv_deceleration       :"+agv_deceleration       +"\n"\
        #     +"agv_RateAcceleration   :"+agv_RateAcceleration   +"\n"\
        #     +"agv_RateDeceleration   :"+agv_RateDeceleration   +"\n"\
        #     +"imu_integralGain       :"+imu_integralGain       +"\n"\
        #     +"imu_GyroOffset_Z       :"+imu_GyroOffset_Z       +"\n"\
        #     +"agv_angleRangeOffset   :"+agv_angleRangeOffset   +"\n"\
        #     +"agv_distanceRangeOffset:"+agv_distanceRangeOffset+"\n"\
        #     +"factor_rectify         :"+factor_rectify         +"\n"\
        #     +"rectifyAngle           :"+rectifyAngle           +"\n"\
        #     +"LW_compensation        :"+LW_compensation        +"\n"\
        #     +"RW_compensation        :"+RW_compensation        +"\n"\
        #     +"LW_offset              :"+LW_offset              +"\n"\
        #     +"RW_offset              :"+RW_offset
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/parameter")
        print("hlly")
        if filename:
            with open(filename, "w", encoding='utf-8') as f:  # "w"覆盖写入 "a+"追加写入
                # f.write("{}\n".format(text))
                found_widget = self.widget_agvParameter.findChildren(eval('QLineEdit'))  # 找出所有QLineEdit对象
                for i in range(len(found_widget)):
                    f.write(found_widget[i].objectName() + ":" + found_widget[i].text() + "\n")
                f.close()
            self.fuc_log("保存成功")

    def fuc_load_parameter(self):
        print("hlly哈利路亚")
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/parameter")
        with open(filename, 'r') as f:
            lines = f.readlines()
            for line in lines:
                if '#' in line:
                    line = line[:line.index('#')]  # 去除每行#后的内容
                if line.strip():  # 去除字符串两端的空白字符（包括空格、换行符和制表符）
                    parameter = line.strip().split(":", 1)
                    search_string = parameter[0]
                    found_widget = self.widget_agvParameter.findChild(QLineEdit, search_string)  # 根据字符串查找widget中的对象
                    if found_widget:
                        found_widget.setText(parameter[1])
            # lines = f.readlines()
            # # for line in lines:
            # #     line_content = line.rstrip("\n").split(":")
            # #     print(line_content[1])
            # self.input_scale_rateToLine     .setText(lines[0].rstrip("\n").split(":")[1])
            # self.input_agv_default_lineSpeed.setText(lines[1].rstrip("\n").split(":")[1])
            # self.input_agv_default_rateSpeed.setText(lines[2].rstrip("\n").split(":")[1])
            # self.input_agv_acceleration     .setText(lines[3].rstrip("\n").split(":")[1])
            # self.input_agv_deceleration     .setText(lines[4].rstrip("\n").split(":")[1])
            # self.input_agv_RateAcceleration .setText(lines[5].rstrip("\n").split(":")[1])
            # self.input_agv_RateDeceleration .setText(lines[6].rstrip("\n").split(":")[1])
            # self.input_imu_integralGain     .setText(lines[7].rstrip("\n").split(":")[1])
            # self.input_imu_offsetZ           .setText(lines[8].rstrip("\n").split(":")[1])
            # self.input_angleRangeOffset     .setText(lines[9].rstrip("\n").split(":")[1])
            # self.input_distanceRangeOffset  .setText(lines[10].rstrip("\n").split(":")[1])
            # self.input_factor_rectify       .setText(lines[11].rstrip("\n").split(":")[1])
            # self.input_rectifyAngle         .setText(lines[12].rstrip("\n").split(":")[1])
            # self.input_LW_compensation      .setText(lines[13].rstrip("\n").split(":")[1])
            # self.input_RW_compensation      .setText(lines[14].rstrip("\n").split(":")[1])
            # self.input_LW_offset            .setText(lines[15].rstrip("\n").split(":")[1])
            # self.input_RW_offset            .setText(lines[16].rstrip("\n").split(":")[1])

    def fuc_nrf24l01_address(self):
        address = int(self.lineEdit_address_nrf24l01.text())
        cmd_id = [0x92, 0x01]
        cmd_data = [0x01, address >> 8 & 0xff, address & 0xff]
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # AGV程序升级

    def fuc_iap_in(self):  # 进入bootloader
        cmd_id = [0x00, 0x00]
        cmd_data = [0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_app_in(self):  # 进入APP（取消升级）
        cmd_id = [0x00, 0x00]
        cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_iap_sendLen(self):  # 发送固件长度
        # 选择固件
        self.iap_filename, _ = QFileDialog.getOpenFileName(None, 'Open File', self.widget_OTA.input_app_address.text())
        if self.iap_filename:
            with open(self.iap_filename, 'rb') as f:
                data = f.read()

            # 发送固件长度
            self.fileLen = len(data)
            self.fuc_log("程序大小为%d个字节" % self.fileLen)
            self.widget_OTA.pgB_upgrade.setValue(0)  # progressBar
            self.widget_OTA.pgB_upgrade.setMaximum(self.fileLen)
            for i in range(len(self.servers_pgBars)):
                self.servers_pgBars[i].setMaximum(self.fileLen)
                # self.servers_pgBars[i].setRange(0, self.fileLen)
                self.servers_pgBars[i].setValue(0)

            lendata = [len(data) >> 24, len(data) >> 16 & 0xff, len(data) >> 8 & 0xff, len(data) & 0xff]
            cmd_id = [0x00, 0x00]
            cmd_data = [0x03] + lendata
            self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_receive_in(self):  # 进入接收固件状态
        cmd_id = [0x00, 0x00]
        cmd_data = [0x02]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_uart_sendBin(self, sendData2):  # 分包发送时回调
        global global_s
        if self.widget_connect.check_connect_device.isChecked():
            global_s.send(sendData2)
        elif self.rxflag:
            self.ser.write(sendData2)
        elif self.flag_server:
            self.server_thread.signal_send_data.emit(sendData2)
        # self.fuc_log('发送完成')
        # self.thread_sendBin.join()

    def fuc_iap_sendBin(self):  # 发送bin文件
        global global_s
        if self.widget_OTA.check_sendBin_pack.isChecked():  # 分包发送
            self.thread_sendBin = thread_sendBin(self.widget_OTA.input_app_address.text())
            self.thread_sendBin.sig_sendBin.connect(self.fuc_uart_sendBin)
            self.thread_sendBin.start()
        else:  # 直接整个文件发送
            self.iap_filename, _ = QFileDialog.getOpenFileName(
                None, 'Open File', self.widget_OTA.input_app_address.text())
            with open(self.iap_filename, 'rb') as f:
                data = f.read()
            # 发送固件
            sendData2 = bytes(data)
            if self.widget_connect.check_connect_device.isChecked():
                global_s.send(sendData2)
            elif self.rxflag:
                self.ser.write(sendData2)
            elif self.flag_server:
                self.server_thread.signal_send_data.emit(sendData2)
            for i in range(len(self.servers_thread)):
                self.servers_thread[i].send_message(sendData2)
            self.fuc_log('发送完成')

    def fuc_updateProgram(self):  # 一键升级
        # 进入IAP(单片机作为服务端时不能自动，需手动重连)
        # self.fuc_iap_in()
        # time.sleep(0.3)

        # 选择固件
        self.iap_filename, _ = QFileDialog.getOpenFileName(None, 'Open File', self.widget_OTA.input_app_address.text())
        with open(self.iap_filename, 'rb') as f:
            data = f.read()

        # 发送固件长度
        self.fileLen = len(data)
        self.widget_OTA.pgB_upgrade.setValue(0)  # progressBar
        self.widget_OTA.pgB_upgrade.setMaximum(self.fileLen)
        self.fuc_log("程序大小为%d个字节" % self.fileLen)
        lendata = [len(data) >> 24, len(data) >> 16 & 0xff, len(data) >> 8 & 0xff, len(data) & 0xff]
        cmd_id = [0x00, 0x00]
        cmd_data = [0x03] + lendata
        self.fuc_serial_send(cmd_id, cmd_data)
        time.sleep(0.3)

        # 进入接收固件状态
        cmd_id = [0x00, 0x00]
        cmd_data = [0x02]
        self.fuc_serial_send(cmd_id, cmd_data)
        self.fuc_log('进入接收固件状态')
        time.sleep(0.3)

        # 发送固件
        # data=data[0:self.packLen]
        sendData2 = bytes(data)
        global global_s
        if self.widget_connect.check_connect_device.isChecked():
            global_s.send(sendData2)
        elif self.rxflag:
            self.ser.write(sendData2)
        elif self.flag_server:
            self.server_thread.signal_send_data.emit(sendData2)
        self.fuc_log('发送完成')

        # 进入APP(需等待接收完成；单片机作为服务端时不能自动，需手动重连)
        # self.fuc_app_in()
        # time.sleep(0.3)

    # endregion
    # region # 校准IMU（陀螺仪）
    def fuc_imu_test(self, cw):
        if (self.flag_imu_test):
            self.flag_imu_test = 0
            self.timer10_imuTest.stop()
        else:
            self.flag_imu_test = 1
            self.angle_count = 1
            self.imu_rotateCount = 0
            if cw == 1:
                self.angleList = [0, 90, 180, -90]
            elif cw == 0:
                self.angleList = [0, -90, -180, 90]
            elif cw == 2:
                self.angleList = [0, 90, 0, -90]
            self.task_imuTest()
            self.timer10_imuTest.start(5000)

    def task_imuTest(self):
        self.input_agv_targetAngle.setText(str(self.angleList[self.angle_count]))
        self.fuc_agv_toAngle()
        self.angle_count += 1
        if (self.angle_count > 3):
            self.angle_count = 0

        self.imu_rotateCount += 1
        self.input_imu_rotateCount.setText(str(self.imu_rotateCount))

    def fuc_set_rateSpeed(self, value):
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = value / 1000
        self.widget_imu.input_rateSpeed.setText(str(rateSpeed))
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_set1_rateSpeed(self):
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = float(self.widget_imu.input_rateSpeed.text())
        maxRateSpeed = 0.7
        if rateSpeed > maxRateSpeed:
            rateSpeed = maxRateSpeed
            self.widget_imu.input_rateSpeed.setText(str(maxRateSpeed))
        elif rateSpeed < -maxRateSpeed:
            rateSpeed = -maxRateSpeed
            self.widget_imu.input_rateSpeed.setText(str(-maxRateSpeed))
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_reset_rateSpeed(self):
        cmd_id = [0x40, 0x60]
        lineSpeed = 0
        rateSpeed = 0
        self.widget_imu.slider_rateSpeed.valueChanged.disconnect(
            self.fuc_set_rateSpeed)  # 先断开滑动条绑定的槽函数，防止input_rateSpeed也被复位为0
        self.widget_imu.slider_rateSpeed.setValue(0)
        self.widget_imu.slider_rateSpeed.valueChanged.connect(self.fuc_set_rateSpeed)
        cmd_data = floatToList(lineSpeed) + floatToList(rateSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_reset_curve(self):
        self.thread_angle.y = [0] * 6
        self.thread_angle.dataNum_current = 0

    def fuc_offset_calibration(self):
        gyroX = self.widget_imu.input_average_gyroX.text()
        gyroY = self.widget_imu.input_average_gyroY.text()
        gyroZ = self.widget_imu.input_average_gyroZ.text()
        self.widget_imu.input_gyroX.setText(gyroX)
        self.widget_imu.input_gyroY.setText(gyroY)
        self.widget_imu.input_gyroZ.setText(gyroZ)
        l_gyroX = floatToList(float(gyroX))
        l_gyroY = floatToList(float(gyroY))
        l_gyroZ = floatToList(float(gyroZ))
        cmd_id = [0x40, 0x23]
        cmd_data = l_gyroX + l_gyroY + l_gyroZ
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # 地图二维码
    def fuc_load_map_DmCode(self):
        filename, _ = QFileDialog.getOpenFileName(
            None, 'Open File', parent_dir_path + "/appDoc/map_DmCode", "Excel Files (*.xlsx);;All Files (*)")
        workbook = openpyxl.load_workbook(filename)
        sheet = workbook.active
        for row in range(2, sheet.max_row + 1):
            self.paint.dotdata.append({
                'pos': (sheet.cell(row, 2).value, sheet.cell(row, 3).value),
                'brush': pg.mkBrush('b'),
                'pen': 'b',
                'data': sheet.cell(row, 1).value
            })
        # print(self.paint.dotdata)
        self.paint.scatter.setData(self.paint.dotdata)

    # endregion
    # region # 二维码标定
    def fuc_page_next(self):
        if (self.widget_tags.stack_tags.currentIndex() == self.widget_tags.stack_tags.count() - 1):
            return
        id = self.widget_tags.stack_tags.currentIndex() + 1
        self.widget_tags.stack_tags.setCurrentIndex(id)
        self.widget_tags.input_page_current.setText(str(id + 1))

    def fuc_page_pre(self):
        if (self.widget_tags.stack_tags.currentIndex() == 0):
            return
        id = self.widget_tags.stack_tags.currentIndex() - 1
        self.widget_tags.stack_tags.setCurrentIndex(id)
        self.widget_tags.input_page_current.setText(str(id + 1))

    def fuc_page_jump(self):
        id = int(self.widget_tags.input_page_current.text()) - 1
        print(id)
        self.widget_tags.stack_tags.setCurrentIndex(id)

    def fuc_clear_Dmcode(self):
        print("hlly")
        self.DMcode_number = 0
        # 删除所有页面
        while self.widget_tags.stack_tags.count():
            self.widget_tags.stack_tags.removeWidget(self.widget_tags.stack_tags.currentWidget())
        self.line_edits = []
        self.checkBoxs = []
        self.dmcode_labels = []

    def fuc_query_DmCode(self):
        self.fuc_clear_Dmcode()
        cmd_id = [0x40, 0x50]
        startAddress = int(self.widget_tags.input_start_address.text())
        num = int(self.widget_tags.input_tags_number.text())
        cmd_data = int16Tolist_big(startAddress) + int16Tolist_big(num)
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_check_DmCode(self):
        if self.widget_tags.check_all.isChecked():
            for i in range(len(self.checkBoxs)):
                self.checkBoxs[i].setChecked(True)
        else:
            for i in range(len(self.checkBoxs)):
                self.checkBoxs[i].setChecked(False)

    def fuc_upload_DmCode(self):
        try:
            self.DMcode_number = int(len(self.line_edits) / 4)
            self.widget_tags.pgB_upload.setMaximum(self.DMcode_number - 1)
            for i in range(self.DMcode_number):
                if self.checkBoxs[i].isChecked():
                    # print(i)

                    DM_tagNumber = int(self.line_edits[4 * i].text())
                    DM_posX = float(self.line_edits[4 * i + 1].text())
                    DM_posY = float(self.line_edits[4 * i + 2].text())
                    DM_ErrorAngle = float(self.line_edits[4 * i + 3].text())
                    # i+=256
                    self.dmcode_labels[i].text()
                    DM_codeNum = int16Tolist_big(int(self.dmcode_labels[i].text()))
                    # print(DM_tagNumber)
                    # print(DM_posX)
                    # print(DM_posY)
                    # print(DM_ErrorAngle)
                    cmd_id = [0x40, 0x51]
                    cmd_data = int32Tolist(DM_tagNumber) + floatToList(DM_posX) + \
                        floatToList(DM_posY) + floatToList(DM_ErrorAngle) + DM_codeNum
                    self.fuc_serial_send(cmd_id, cmd_data)
                    sendInterval = int(self.widget_tags.input_sendInterval.text()) / 1000
                    # print(sendInterval)#发送时间间隔
                    time.sleep(sendInterval)
                # self.widget_tags.pgB_upload.setValue(i)
                self.signal.pgB_DmCode.emit(i)
        except:
            self.widget_tags.info.setText(get_time() + " " + '请先打开文件或者查询！')
            self.widget_tags.info.setStyleSheet("color: red;")

    def fuc_set_DmCode(self):
        thread_upload_DmCode = threading.Thread(target=self.fuc_upload_DmCode)
        thread_upload_DmCode.start()

    def fuc_save_DmCode(self):
        # 获取用户选择的文件路径和名称
        try:
            file_name, _ = QFileDialog.getSaveFileName(
                self, "Save File", parent_dir_path + "/appDoc/DmCode", "Excel Files (*.xlsx);;All Files (*)")
        except:
            wb = openpyxl.Workbook()
            wb.save(f"{file_name}.xlsx")
        if not file_name:
            return
        # 创建Workbook对象并写入数据
        try:
            wb = openpyxl.load_workbook(file_name)
        except:
            wb = openpyxl.Workbook()
        ws = wb.active
        title = ['序号', 'tag值', 'X(mm)', 'Y(mm)', '误差角度(°)']
        for col in range(5):
            cell_value = title[col]
            ws.cell(row=1, column=col + 1, value=cell_value)
        try:
            for row in range(self.DMcode_number):
                cell_value = int(self.dmcode_labels[row].text())
                ws.cell(row=row + 2, column=1, value=cell_value)
                for col in range(4):
                    if self.line_edits[4 * row + col].text() != 'None':
                        cell_value = float(self.line_edits[4 * row + col].text())
                        ws.cell(row=row + 2, column=col + 2, value=cell_value)
        except:
            self.widget_tags.info.setText(get_time() + " " + '保存数据为空！')
        # 保存文件
        try:
            wb.save(file_name)
            self.widget_tags.info.setText(get_time() + " " + "保存成功！")
        except Exception as e:
            self.widget_tags.info.setText(get_time() + " " + str(e))
            self.widget_tags.info.setStyleSheet("color: red;")

    def fuc_load_DmCode(self):
        print("hlly")
        self.fuc_clear_Dmcode()
        # 打开Excel文件
        # filename=parent_dir_path+"/appDoc/DmCode/abc.xlsx"
        filename, _ = QFileDialog.getOpenFileName(
            None, 'Open File', parent_dir_path + "/appDoc/DmCode", "Excel Files (*.xlsx);;All Files (*)")
        workbook = openpyxl.load_workbook(filename)

        # 选择工作表
        sheet = workbook.active

        # 遍历所有行和列，读取单元格数据
        for row in range(1, sheet.max_row):
            row -= 1
            # 创建新页面
            if row % self.page_codeNum == 0:
                self.new_page = QWidget()
                self.grid_layout = QGridLayout()
                self.new_page.setLayout(self.grid_layout)
                self.widget_tags.stack_tags.addWidget(self.new_page)
                # labelList=["","序号","二维码编码","坐标X","坐标Y","补偿角度"]
                labelList = ["", "序号", "二维码编码", "坐标X", "坐标Y", "补偿角度"]
                for i in range(3):
                    for j in range(6):
                        label = QLabel(labelList[j])
                        self.grid_layout.addWidget(label, 0, 6 * i + j)

            # 创建输入框
            check = QCheckBox()
            check.setChecked(True)
            self.checkBoxs.append(check)
            label = QLabel(str(sheet.cell(row + 2, 1).value))
            self.dmcode_labels.append(label)
            self.grid_layout.addWidget(check, row % self.page_codeNum // 3 + 1, 6 * (row % self.page_codeNum % 3))
            self.grid_layout.addWidget(label, row % self.page_codeNum // 3 + 1, 6 * (row % self.page_codeNum % 3) + 1)
            for j in range(4):
                line_edit = QLineEdit(self)
                line_edit.setObjectName(f'line_edit_{4*row+j}')
                self.line_edits.append(line_edit)
                self.grid_layout.addWidget(line_edit, row % self.page_codeNum // 3 + 1,
                                           6 * (row % self.page_codeNum % 3) + j + 2)
                self.line_edits[4 * row + j].setText(str(sheet.cell(row + 2, j + 2).value))
            self.DMcode_number += 1
        # 关闭Excel文件
        workbook.close()
        self.widget_tags.info.setText(get_time() + " " + "加载完成！")

    # endregion
    # region # 调度模拟
    def fuc_cmdNumChange(self, index):  # 命令号切换联动命令名切换
        # self.layout_cmd.layout().indexOf(self.sender())
        widget_index = self.layout_cmd.indexOf(self.sender())
        widget = self.layout_cmd.itemAt(widget_index + 10).widget()  # 10条指令，对应后一列name
        widget.setCurrentIndex(index)

    def fuc_cmdNameChange(self, index):  # 命令名切换联动命令号切换
        # self.layout_cmd.layout().indexOf(self.sender())
        widget_index = self.layout_cmd.indexOf(self.sender())
        widget = self.layout_cmd.itemAt(widget_index - 10).widget()  # 10条指令，对应前一列num
        widget.setCurrentIndex(index)

    def fuc_change_cmdNum(self, state):  # 命令号自动递增
        if state == 2:  # 2 表示 Qt.Checked
            # self.layout_cmd.layout().indexOf(self.sender())
            widget_index = self.layout_cmd.indexOf(self.sender())
            widget = self.layout_cmd.itemAt(widget_index + 10).widget()  # 10条指令，对应后一列name
            if widget.text() != "00":
                widget.setText(str(self.cmdNum))
                self.cmdNum += 1
                if self.cmdNum >= 9999:
                    self.cmdNum = 0

    def fuc_check_cmd_all(self):  # 选择所有指令
        widget = self.layout_cmd.itemAt(0).widget()  # 对应全选框
        if widget.isChecked():
            for i in range(self.num_cmd):
                widget = self.layout_cmd.itemAt(self.cmd_column + i).widget()
                widget.setChecked(True)
        else:
            for i in range(self.num_cmd):
                widget = self.layout_cmd.itemAt(self.cmd_column + i).widget()
                widget.setChecked(False)

    def fuc_cmdSave(self):  # 批量保存指令
        # 保存为
        # 获取用户选择的文件路径和名称
        try:
            file_name, _ = QFileDialog.getSaveFileName(
                self, "Save File", parent_dir_path + "/appDoc/cmd", "Excel Files (*.xlsx);;All Files (*)")
        except:
            wb = openpyxl.Workbook()
            wb.save(f"{file_name}.xlsx")
        if not file_name:
            return
        # 创建Workbook对象并写入数据
        try:
            wb = openpyxl.load_workbook(file_name)
        except:
            wb = openpyxl.Workbook()
        ws = wb.active
        title = ['选中', '指令码', '指令名', '参数1-1', '参数1-2', '参数1-3', '参数2-1', '参数2-2', '参数2-3', '参数3-1']
        for col in range(10):
            cell_value = title[col]
            ws.cell(row=1, column=col + 1, value=cell_value)
        num_cmd = self.num_cmd
        for i in range(num_cmd):
            for j in range(10):
                widget = self.layout_cmd.itemAt(self.cmd_column + j * 10 + i).widget()
                if j == 0:
                    cell_value = "True" if widget.isChecked() else "False"
                    ws.cell(row=1 + i + 1, column=1 + j, value=cell_value)
                elif j == 2:
                    cell_value = widget.currentText()
                    ws.cell(row=1 + i + 1, column=1 + j, value=cell_value)
                else:
                    if widget.text() == "":
                        cell_value = "0"
                    else:
                        cell_value = widget.text()
                    ws.cell(row=1 + i + 1, column=1 + j, value=cell_value)
        # 保存文件
        try:
            wb.save(file_name)
            self.widget_tags.info.setText(get_time() + " " + "保存成功！")
        except Exception as e:
            self.widget_tags.info.setText(get_time() + " " + str(e))
            self.widget_tags.info.setStyleSheet("color: red;")
        # 保存为txt
        # filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path+"/appDoc/cmd")
        # print(filename)
        # num_cmd=self.num_cmd
        # with open(filename, "w",encoding='utf-8') as f:

        #     for i in range(num_cmd):
        #         for j in range(10):
        #             widget = self.layout_cmd.itemAt(self.cmd_column+j*10+i).widget()
        #             if j==0:
        #                 f.write(("True" if widget.isChecked() else "False")+"  ")
        #             elif j==2 :
        #                 f.write(widget.currentText()+"  ")
        #             else:
        #                 if widget.text()=="":
        #                     f.write("0  ")
        #                 else:f.write(widget.text()+"  ")
        #         f.write("\n")
        self.fuc_log("保存完成")

    def fuc_cmdLoad(self):  # 批量加载指令
        filename, _ = QFileDialog.getOpenFileName(
            None, 'Open File', parent_dir_path + "/appDoc/cmd", "Excel Files (*.xlsx);;All Files (*)")
        workbook = openpyxl.load_workbook(filename)
        sheet = workbook.active
        for row in range(2, sheet.max_row + 1):
            # print(row)
            cellvalue = str(sheet.cell(row, 1).value)
            print(cellvalue)
            for i in range(10):
                widget = self.layout_cmd.itemAt(self.cmd_column + i * 10 + row - 2).widget()
                if i == 0:
                    widget.setChecked(True if str(sheet.cell(row, i + 1).value) == "True" else False)
                elif i == 2:
                    widget.setCurrentText(str(sheet.cell(row, i + 1).value))
                else:
                    try:
                        widget.setText(str(sheet.cell(row, i + 1).value))
                    except:
                        widget.setText('')
        workbook.close()
        # 从txt文件中读取数据
        # filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc//cmd")
        # print(filename)
        # with open(filename, "r", encoding='utf-8') as f:
        #     line_number = 0
        #     for line in f.readlines():
        #         line = line.strip().split("  ")
        #         # print(line)
        #         for i in range(10):
        #             widget = self.layout_cmd.itemAt(self.cmd_column + i * 10 + line_number).widget()
        #             if i == 0: widget.setChecked(True if line[i] == "True" else False)
        #             elif i == 2: widget.setCurrentText(line[i])
        #             else:
        #                 try:
        #                     widget.setText(line[i])
        #                 except:
        #                     widget.setText('')
        #         line_number += 1
        self.fuc_log("加载完成")

    def fuc_sendUiCmd_TCS(self, ID=1, name="GO", p1=0, p2=0, p3=0, p4=0, p5=0, p6=0, p7=0):
        num_list = [ID >> 8 & 0xff, ID & 0xff]
        name_list = [name.encode('utf-8')[0], name.encode('utf-8')[1]]
        tem_cmd = num_list + name_list
        p = [p1, p2, p3, p4, p5, p6, p7]
        p_list = [0, 0, 0, 0, 0, 0, 0]
        for j in range(7):
            p_list[j] = [p[j] >> 24, p[j] >> 16 & 0xff, p[j] >> 8 & 0xff, p[j] & 0xff]
            tem_cmd += p_list[j]
        self.fuc_sendData_TCS(tem_cmd)

    def fuc_sendCmd_TCS(self):
        S_cmd = []
        num_cmd = self.num_cmd
        for i in range(num_cmd):
            widget1 = self.layout_cmd.itemAt(self.cmd_column + i).widget()
            widget = self.layout_cmd.itemAt(self.cmd_column + (1) * num_cmd + i).widget()
            if (widget1.isChecked()):
                num = int(widget.text())
            else:
                num = 0
            num_list = [num >> 8 & 0xff, num & 0xff]
            widget = self.layout_cmd.itemAt(self.cmd_column + (2) * num_cmd + i).widget()
            name = widget.currentText()
            name_list = [name.encode('utf-8')[0], name.encode('utf-8')[1]]
            tem_cmd = num_list + name_list
            p = [0, 0, 0, 0, 0, 0, 0]
            p_list = [0, 0, 0, 0, 0, 0, 0]
            for j in range(7):
                widget = self.layout_cmd.itemAt(self.cmd_column + (3 + j) * num_cmd + i).widget()
                if (widget.text() == ""):
                    p[j] = 0
                else:
                    p[j] = int(widget.text())
                if (p[j] < 0):
                    p[j] = 0x100000000 + p[j]
                # p[j]_list=[p[j]>>24,p[j]>>16&0xff,p[j]>>8&0xff,p[j]&0xff]# int32_t 单片机内存存储时高位在后
                # p_list[j]=[p[j]&0xff,p[j]>>8&0xff,p[j]>>16&0xff,p[j]>>24]
                p_list[j] = [p[j] >> 24, p[j] >> 16 & 0xff, p[j] >> 8 & 0xff, p[j] & 0xff]
                tem_cmd += p_list[j]
            S_cmd += tem_cmd
            # print(S_cmd)
        # self.sendData_to_clients(S_cmd)
        self.fuc_sendData_TCS(S_cmd)

    def fuc_sendData_TCS(self, S_cmd):  # 向服务器（控制板）发送数据
        Block_counter = '0001'
        length = str(len(S_cmd) + 24).zfill(5)  # 报文尾长度不计算在内
        # print(length)
        type = 'N'
        source = 'R'
        target = 'S'
        Distribution_code = 'ALFF'
        identifier = 'FP'
        Function_point = '003631'
        Telegram = Block_counter + length + type + source + \
            target + Distribution_code + identifier + Function_point
        byte_Telegram = Telegram.encode()
        byte_Telegram += bytes(S_cmd)
        # print(byte_Telegram)
        byte_Telegram += bytes([0])
        global global_s
        if self.widget_connect.check_connect_device.isChecked():
            global_s.send(byte_Telegram)
        if (self.widget_log.check_showsSendMessage.isChecked()):  # 日志打印发送数据
            self.fuc_log("Tx:" + "  " + HexStrAddSpace(byte_Telegram.hex()))

    def fuc_switchAuthority(self):
        cmd_id = [0x80, 0x01]
        if self.check_switchAuthority.isChecked():
            cmd_data = [0x02]
            self.fuc_log('切换为调度控制')
        else:
            cmd_data = [0x01]
            self.fuc_log('切换为触摸屏控制')
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # 调度数据监测
    def tcs_load_data(self):
        print("tcs_load_data")
        """解析日志文件，提取时间戳和二进制数据"""
        entries = []
        pattern = r'(\d{2}:\d{2}:\d{2}\.\d{3})\s+(b\'.*\')'  # 匹配时间戳和二进制数据

        default_log_dir = self.widget_tcs_watch.lineEdit_data_path.text()
        file_path, _ = QFileDialog.getOpenFileName(self, "选择日志文件", default_log_dir, "日志文件 (*.txt);;所有文件 (*)")
        if not file_path:
            return
        with open(file_path, 'r', encoding='utf-8') as file:
            for line in file:
                match = re.match(pattern, line.strip())
                if match:
                    timestamp_str, data_str = match.groups()
                    # 将时间戳转为datetime对象（方便后续排序或计算间隔）
                    timestamp = datetime.strptime(timestamp_str, "%H:%M:%S.%f")
                    # 将二进制字符串转为实际bytes对象（去掉前缀b'和末尾'）
                    data_bytes = eval(data_str)  # 注意：eval有安全风险，仅限可信数据
                    entries.append({"timestamp": timestamp, "data": data_bytes})
        start_time = entries[0]["timestamp"]
        end_time = entries[-1]["timestamp"]
        if start_time and end_time:
            self.widget_tcs_watch.timeEdit_start.setDateTime(start_time)
            self.widget_tcs_watch.timeEdit_end.setDateTime(end_time)
        else:
            QMessageBox.warning(self, "警告", "未找到有效时间戳！")
        self.tcs_datas = entries
        return entries

    def tcs_play_data(self):
        print("tcs_play_data")
        self.widget_tcs_watch.checkBox_record_data.setChecked(False)
        self.widget_connect.check_connect_device.setChecked(False)
        if not self.thread_playData:
            self.thread_playData = thread_playData()
            self.thread_playData.send_data.connect(self.fuc_data_process)
            self.thread_playData.sig_progress.connect(self.tcs_update_progress)
        if not self.tcs_datas:
            self.tcs_load_data()
        start_time = self.widget_tcs_watch.timeEdit_start.dateTime().toPyDateTime()
        end_time = self.widget_tcs_watch.timeEdit_end.dateTime().toPyDateTime()
        self.thread_playData.datas = [
            data for data in self.tcs_datas
            if start_time <= data["timestamp"] <= end_time
        ]
        self.widget_tcs_watch.lineEdit_play_total.setText(str(len(self.thread_playData.datas)))
        self.thread_playData.multiSpeed = 1/float(self.widget_tcs_watch.lineEdit_play_multiSpeed.text())
        self.thread_playData.start()

    def tcs_play_pause(self):
        print("tcs_play_pause")
        self.thread_playData.flag_run = False

    def tcs_play_continue(self):
        print("tcs_play_continue")
        self.thread_playData.flag_run = True

    def tcs_play_multiSpeed(self):  # 倍速播放
        print("tcs_play_multiSpeed")
        if self.thread_playData:
            self.thread_playData.multiSpeed = 1/float(self.widget_tcs_watch.lineEdit_play_multiSpeed.text())

    def tcs_update_progress(self, progress):  # 更新播放进度
        self.widget_tcs_watch.lineEdit_play_current.setText(str(progress))

    def tcs_jump_progress(self):  # 进度跳转
        current = int(self.widget_tcs_watch.lineEdit_play_current.text())
        self.thread_playData.jumpProgress = current

    def tcs_record_data(self, data):
        print("tcs_record_data")
        filepath = "appDoc/record_data/{0}.txt".format(self.widget_log.input_record_name.text())
        log = get_time() + "  " + str(data)
        with open(filepath, "a+", encoding='utf-8') as f:
            f.write("{}\n".format(log))
    # endregion
    # region # 调度日志分析

    def extract_agv_numbers(self, log_lines):  # 提取所有不重复的AGV编号
        agv_pattern = re.compile(r'AGV\d+')  # 匹配 AGV1, AGV5, AGV9 等
        agv_numbers = set()  # 使用集合去重

        for line in log_lines:
            match = agv_pattern.search(line)
            if match:
                agv_numbers.add(match.group())  # 添加到集合

        return sorted(agv_numbers)  # 返回排序后的列表

    def extract_err_numbers(self, log_lines):  # 提取日志中所有不重复的 RFID 代码
        # 使用正则表达式匹配 "rfid is XX"（XX 为数字）
        err_numbers = set()
        err_pattern = re.compile(r"rfid is (\d+)")
        for line in log_lines:
            match = err_pattern.search(line)
            if match:
                err_numbers.add(match.group())  # 添加到集合

        return sorted(err_numbers)  # 返回排序后的列表

    def extract_timestamp(self, log_line):  # 提取日志行中的起始和结束时间戳
        """从日志行中提取时间戳并转换为QDateTime"""
        # 匹配时间戳格式：2025-10-06 17:12:11,511
        pattern = r'^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}),\d{3}'
        match = re.match(pattern, log_line)

        if match:
            # 转换为 QDateTime 格式（去掉毫秒）
            datetime_str = match.group(1)  # 例如 "2025-10-06 17:12:11"
            datetime = QDateTime.fromString(datetime_str, "yyyy-MM-dd HH:mm:ss")
            return datetime
        return None

    def log_analyse_select_log(self):  # 加载日志文件
        default_log_dir = self.widget_log_analyse.lineEdit_log_path.text()
        file_path, _ = QFileDialog.getOpenFileName(self, "选择日志文件", default_log_dir, "日志文件 (*.log.*);;所有文件 (*)")

        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as file:
                    lines = [line.strip() for line in file if line.strip()]  # 过滤空行

                if not lines:
                    QMessageBox.warning(self, "警告", "文件为空！")
                    return
                self.filepath_log_analyse = file_path
                # 提取所有不重复的AGV编号
                agv_numbers = self.extract_agv_numbers(lines)
                if agv_numbers:
                    self.widget_log_analyse.comboBox_agv_num.clear()
                    self.widget_log_analyse.comboBox_agv_num.addItems(sorted(agv_numbers))  # 按编号排序
                else:
                    QMessageBox.warning(self, "警告", "未找到AGV编号！")

                # 提取所有不重复的错误编号
                err_numbers = self.extract_err_numbers(lines)
                if err_numbers:
                    self.widget_log_analyse.comboBox_err_num.clear()
                    self.widget_log_analyse.comboBox_err_num.addItems(sorted(err_numbers))  # 按编号排序
                else:
                    self.widget_log_analyse.checkBox_err_num.setChecked(False)
                    # QMessageBox.warning(self, "警告", "未找到错误编号！")

                # 提取第一行和最后一行的时间戳
                start_time = self.extract_timestamp(lines[0])
                end_time = self.extract_timestamp(lines[-1])

                if start_time and end_time:
                    self.widget_log_analyse.dateTimeEdit_start.setDateTime(start_time)
                    self.widget_log_analyse.dateTimeEdit_end.setDateTime(end_time)
                else:
                    QMessageBox.warning(self, "警告", "未找到有效时间戳！")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"无法加载文件:\n{str(e)}")

    def log_analyse_start(self):
        if self.filepath_log_analyse:
            self.filtered_logs = []
            agv_filter = self.widget_log_analyse.comboBox_agv_num.currentText()
            rfid_filter = self.widget_log_analyse.comboBox_err_num.currentText()
            start_time = self.widget_log_analyse.dateTimeEdit_start.dateTime().toPyDateTime()
            end_time = self.widget_log_analyse.dateTimeEdit_end.dateTime().toPyDateTime()

            # 去重
            time_pattern = re.compile(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}: ')
            seen = set()
            with open(self.filepath_log_analyse, 'r', encoding='utf-8') as file:
                for line in file:
                    line = line.strip()
                    if not line:
                        continue

                    # 解析日志时间、AGV编号和RFID
                    try:
                        # 提取AGV编号并过滤（如 "AGV5"）
                        if self.widget_log_analyse.checkBox_agv_num.isChecked():
                            agv_match = re.search(r': (AGV\d+)', line)
                            log_agv = agv_match.group(1) if agv_match else None
                            if log_agv != agv_filter:
                                print(log_agv)
                                continue

                        # 提取RFID代码并过滤
                        if self.widget_log_analyse.checkBox_err_num.isChecked():
                            rfid_match = re.search(r'rfid is (\d+)', line)
                            log_rfid = rfid_match.group() if rfid_match else None
                            if log_rfid != rfid_filter:
                                continue

                        # 提取时间并过滤（格式：2025-10-06 06:19:58,385）
                        if self.widget_log_analyse.checkBox_time_start.isChecked() or self.widget_log_analyse.checkBox_time_end.isChecked():
                            timestamp_str = re.search(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3}', line).group()
                            log_time = datetime.strptime(timestamp_str, "%Y-%m-%d %H:%M:%S,%f")
                            if log_time <= start_time and self.widget_log_analyse.checkBox_time_start.isChecked():
                                continue
                            if log_time >= end_time and self.widget_log_analyse.checkBox_time_end.isChecked():
                                continue
                        if self.widget_log_analyse.checkBox_repeat_remove.isChecked():  # 去重
                            content = time_pattern.sub('', line)
                            if content not in seen:
                                seen.add(content)
                                self.filtered_logs.append(line)
                        else:
                            self.filtered_logs.append(line)
                    except Exception as e:
                        print(f"解析日志行失败: {line}, 错误: {e}")
                        continue
            # 显示过滤后的日志
            self.widget_log_analyse.textBrowser_log.clear()
            self.widget_log_analyse.textBrowser_log.append("\n".join(self.filtered_logs))
        else:
            QMessageBox.warning(self, "警告", "文件为空！")

    def log_analyse_addToMap(self):
        xy_pattern = re.compile(r'X = (\d+), Y = (\d+)')
        coordinates = []

        for line in self.filtered_logs:
            match = xy_pattern.search(line)
            if match:
                x, y = float(match.group(1)), int(match.group(2))
                coordinates.append((x / 1000, y / 1000))

        for x, y in coordinates:
            print(f"x: {x}, y: {y}")
            self.paint.dotdata.append({
                'pos': (float(f"{x:.2f}"), float(f"{y:.2f}")),
                'brush': QBrush(QColor('blue')),  # 等价于 pg.mkBrush('b')
                'pen': 'b',  # 画笔颜色
                'data': f"x: {x}, y: {y}"  # 可选附加数据（如时间戳）
            })
        # self.paint.dotdata = json.loads(content)
        self.paint.scatter.setData(self.paint.dotdata)

    # endregion
    # region # 更新摄像头画面

    def update_DM(self, number, x, y, h, w):
        # print(number)
        self.widget_camera.input_dm_number.setText(number)
        self.widget_camera.input_dm_x.setText(str(x))
        self.widget_camera.input_dm_y.setText(str(y))
        self.widget_camera.input_dm_h.setText(str(h))
        self.widget_camera.input_dm_w.setText(str(w))
        angle = math.atan2(h, w) / math.pi * 180
        angle = round(angle, 2)
        angle += 45
        if (angle > 180):
            angle = angle - 360
        elif (angle < -180):
            angle = angle + 360
        self.widget_camera.input_dm_angle.setText(str(angle))

    def update_image(self, pixmap):
        self.widget_camera.label_video.setPixmap(QPixmap.fromImage(pixmap))
        # # 将QImage转换为QPixmap
        # qpixmap = QPixmap.fromImage(pixmap)
        # # 将QPixmap转换为OpenCV的Mat对象
        # mat = cv2.cvtColor(qpixmap.toImage().copy(), cv2.COLOR_BGR2RGB)
        # cv2.imshow("Image", mat)
        # cv2.waitKey(0)

    def fuc_decode(self):
        if self.flag_camera:
            self.video_thread.fuc_decode()

    def update_frame(self):
        # 读取摄像头图像
        ret, frame = self.cap.read()
        if not ret:  # 如果读取失败，说明摄像头不存在或无画面
            print("摄像头不存在或无画面")

        # 将图像从OpenCV格式转换为QImage格式
        try:
            rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            h, w, ch = rgb_image.shape
            bytes_per_line = ch * w
            q_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888)

            # 将QImage格式转换为QPixmap格式并显示在标签上
            pixmap = QPixmap.fromImage(q_image)
            self.widget_camera.label_video.setPixmap(pixmap)
        except:
            print("cv2.cvtColor() error!")
            self.timer12.stop()

    # endregion
    # region # 设置窗口大小
    def resizeWindow(self):
        self.resize(100, 100)
        self.timer13.stop()
        size = self.size()
        print("窗口宽度：", size.width())
        print("窗口高度：", size.height())

    # def timer_Test(self):
    #     print("timerTest")
    # def resizeEvent(self, event):
    # size = self.size()
    # print(f"窗口宽度：{size.width()}, 窗口高度：{size.height()}")
    # endregion
    # region # 实现鼠标拖拽功能
    def mousePressEvent(self, event):
        self.pressX = event.x()  # 记录鼠标按下的时候的坐标
        self.pressY = event.y()

    def mouseMoveEvent(self, event):
        try:
            x = event.x()
            y = event.y()  # 获取移动后的坐标
            moveX = x - self.pressX
            moveY = y - self.pressY  # 计算移动了多少

            positionX = self.frameGeometry().x() + moveX
            positionY = self.frameGeometry().y() + moveY  # 计算移动后主窗口在桌面的位置
            self.move(positionX, positionY)  # 移动主窗口
        except:
            pass

    # endregion
    # region # 麦克纳姆轮小车调试
    def toggle_widget_mecanum(self, checked):
        if checked:
            self.widget_mecanum.show()
        else:
            self.widget_mecanum.hide()

    def mecanum_mode_control(self):  # 控制模式切换
        cmd_id = [0x40, 0x51]
        if self.widget_mecanum.check_mode_control.isChecked():
            cmd_data = [1]
        else:
            cmd_data = [0]
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_stop(self):
        cmd_id = [0x40, 0x63]
        cmd_data = [0x00, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)
        self.widget_mecanum.S_setPWM_LF.setValue(0)
        self.widget_mecanum.S_setPWM_RF.setValue(0)
        self.widget_mecanum.S_setPWM_LB.setValue(0)
        self.widget_mecanum.S_setPWM_RB.setValue(0)
        self.widget_mecanum.S_setSpeed_LF.setValue(0)
        self.widget_mecanum.S_setSpeed_RF.setValue(0)
        self.widget_mecanum.S_setSpeed_LB.setValue(0)
        self.widget_mecanum.S_setSpeed_RB.setValue(0)

        self.widget_mecanum.S_speedX.setValue(0)
        self.widget_mecanum.S_speedY.setValue(0)
        self.widget_mecanum.S_speedZ.setValue(0)

    def mecanum_setMaxspeed(self):
        maxspeed = int(self.widget_mecanum.input_maxSpeed.text())
        self.widget_mecanum.S_setSpeed_LF.setMaximum(maxspeed)
        self.widget_mecanum.S_setSpeed_RF.setMaximum(maxspeed)
        self.widget_mecanum.S_setSpeed_LB.setMaximum(maxspeed)
        self.widget_mecanum.S_setSpeed_RB.setMaximum(maxspeed)
        self.widget_mecanum.S_setSpeed_LF.setMinimum(-maxspeed)
        self.widget_mecanum.S_setSpeed_RF.setMinimum(-maxspeed)
        self.widget_mecanum.S_setSpeed_LB.setMinimum(-maxspeed)
        self.widget_mecanum.S_setSpeed_RB.setMinimum(-maxspeed)
        # self.widget_mecanum.S_speedX.setMaximum(maxspeed)
        # self.widget_mecanum.S_speedX.setMinimum(-maxspeed)

    def mecanum_setPWM1(self, wheel):  # 通过文本输入框设置
        if wheel == 'LF':
            valueLF = int(self.widget_mecanum.input_setPWM_LF.text())
            self.widget_mecanum.S_setPWM_LF.setValue(valueLF)
            pwmLF = int16Tolist(valueLF)
            self.mecanum_pwm[0] = pwmLF[0]
            self.mecanum_pwm[1] = pwmLF[1]
        elif wheel == 'RF':
            valueRF = int(self.widget_mecanum.input_setPWM_RF.text())
            self.widget_mecanum.S_setPWM_RF.setValue(valueRF)
            pwmRF = int16Tolist(valueRF)
            self.mecanum_pwm[2] = pwmRF[0]
            self.mecanum_pwm[3] = pwmRF[1]
        elif wheel == 'LB':
            valueLB = int(self.widget_mecanum.input_setPWM_LB.text())
            self.widget_mecanum.S_setPWM_LB.setValue(valueLB)
            pwmLB = int16Tolist(valueLB)
            self.mecanum_pwm[4] = pwmLB[0]
            self.mecanum_pwm[5] = pwmLB[1]
        elif wheel == 'RB':
            valueRB = int(self.widget_mecanum.input_setPWM_RB.text())
            self.widget_mecanum.S_setPWM_RB.setValue(valueRB)
            pwmRB = int16Tolist(valueRB)
            self.mecanum_pwm[6] = pwmRB[0]
            self.mecanum_pwm[7] = pwmRB[1]

        print('fuc_change_wheel_pwm1')
        cmd_id = [0x40, 0x61]
        cmd_data = self.mecanum_pwm
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_setPWM(self):  # 通过滑动条设置
        valueLF = self.widget_mecanum.S_setPWM_LF.value()
        valueRF = self.widget_mecanum.S_setPWM_RF.value()
        valueLB = self.widget_mecanum.S_setPWM_LB.value()
        valueRB = self.widget_mecanum.S_setPWM_RB.value()
        # self.widget_mecanum.label_wheel_LF.setText(str(valueLF))
        # self.widget_mecanum.label_wheel_RF.setText(str(valueRF))
        # self.widget_mecanum.label_wheel_LB.setText(str(valueLB))
        # self.widget_mecanum.label_wheel_RB.setText(str(valueRB))
        self.widget_mecanum.input_setPWM_LF.setText(str(valueLF))
        self.widget_mecanum.input_setPWM_RF.setText(str(valueRF))
        self.widget_mecanum.input_setPWM_LB.setText(str(valueLB))
        self.widget_mecanum.input_setPWM_RB.setText(str(valueRB))

        pwmLF = int16Tolist(valueLF)
        pwmRF = int16Tolist(valueRF)
        pwmLB = int16Tolist(valueLB)
        pwmRB = int16Tolist(valueRB)
        cmd_id = [0x40, 0x61]
        cmd_data = pwmLF + pwmRF + pwmLB + pwmRB
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_setRPM1(self):  # 通过文本输入框设置目标转速
        valueLF = int(self.widget_mecanum.input_setSpeed_LF.text())
        valueRF = int(self.widget_mecanum.input_setSpeed_RF.text())
        valueLB = int(self.widget_mecanum.input_setSpeed_LB.text())
        valueRB = int(self.widget_mecanum.input_setSpeed_RB.text())
        self.widget_mecanum.S_setSpeed_LF.setValue(valueLF)
        self.widget_mecanum.S_setSpeed_RF.setValue(valueRF)
        self.widget_mecanum.S_setSpeed_LB.setValue(valueLB)
        self.widget_mecanum.S_setSpeed_RB.setValue(valueRB)
        speedLF = int16Tolist(valueLF)
        speedRF = int16Tolist(valueRF)
        speedLB = int16Tolist(valueLB)
        speedRB = int16Tolist(valueRB)
        cmd_id = [0x40, 0x62]
        cmd_data = speedLF + speedRF + speedLB + speedRB
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_setRPM(self):  # 通过滑动条设置目标转速
        valueLF = self.widget_mecanum.S_setSpeed_LF.value()
        valueRF = self.widget_mecanum.S_setSpeed_RF.value()
        valueLB = self.widget_mecanum.S_setSpeed_LB.value()
        valueRB = self.widget_mecanum.S_setSpeed_RB.value()
        self.widget_mecanum.input_setSpeed_LF.setText(str(valueLF))
        self.widget_mecanum.input_setSpeed_RF.setText(str(valueRF))
        self.widget_mecanum.input_setSpeed_LB.setText(str(valueLB))
        self.widget_mecanum.input_setSpeed_RB.setText(str(valueRB))
        speedLF = int16Tolist(valueLF)
        speedRF = int16Tolist(valueRF)
        speedLB = int16Tolist(valueLB)
        speedRB = int16Tolist(valueRB)
        cmd_id = [0x40, 0x62]
        cmd_data = speedLF + speedRF + speedLB + speedRB
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_setSpeed(self):  # 通过滑动条设置目标速度
        value = self.widget_mecanum.S_speedX.value()
        self.widget_mecanum.input_speedX.setText(str(value))
        if self.widget_mecanum.check_speedX.isChecked():
            self.mecanum_move(value)

    def mecanum_setSpeed1(self):  # 通过文本输入框设置目标速度
        value = int(self.widget_mecanum.input_speedX.text())
        self.widget_mecanum.S_speedX.setValue(value)
        if self.widget_mecanum.check_speedX.isChecked():
            self.mecanum_move(value)

    def mecanum_move(self, value):
        self.widget_mecanum.input_setSpeed_LF.setText(str(value))
        self.widget_mecanum.input_setSpeed_RF.setText(str(value))
        self.widget_mecanum.input_setSpeed_LB.setText(str(value))
        self.widget_mecanum.input_setSpeed_RB.setText(str(value))
        self.widget_mecanum.S_setSpeed_LF.setValue(value)
        self.widget_mecanum.S_setSpeed_RF.setValue(value)
        self.widget_mecanum.S_setSpeed_LB.setValue(value)
        self.widget_mecanum.S_setSpeed_RB.setValue(value)
        cmd_id = [0x40, 0x62]
        speed = int16Tolist(value)
        cmd_data = speed + speed + speed + speed
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_forward(self):
        value = self.widget_mecanum.S_speedX.value()
        if value < 0:
            value = -value
            self.widget_mecanum.S_speedX.setValue(value)
            self.widget_mecanum.input_speedX.setText(str(value))
        self.mecanum_move(value)

    def mecanum_back(self):
        value = self.widget_mecanum.S_speedX.value()
        if value > 0:
            value = -value
            self.widget_mecanum.S_speedX.setValue(value)
            self.widget_mecanum.input_speedX.setText(str(value))
        self.mecanum_move(value)

    def mecanum_pid_read(self):  # 读取PID参数
        cmd_id = [0x40, 0x71]
        cmd_data = [0]
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_pid_set(self):  # 设置PID参数
        cmd_id = [0x40, 0x72]
        cmd_data = []
        for item in self.pid_inputs:
            cmd_data += floatToList_B(float(item.text()))
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_pid_save(self):  # 保存PID参数到本地
        makdir(parent_dir_path + "/appDoc/mecanum/pid")
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', parent_dir_path + "/appDoc/mecanum/pid")
        if filename:
            with open(filename, "w", encoding='utf-8') as f:
                cmd_data = [float(item.text()) for item in self.pid_inputs]
                f.write(str(cmd_data))
                self.fuc_log('保存成功')

    def mecanum_pid_load(self):  # 加载本地PID参数
        directory = parent_dir_path + "/appDoc/mecanum/pid"
        makdir(directory)
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', directory)
        if filename:
            with open(filename, "r", encoding='utf-8') as f:
                for line in f.readlines():
                    line = ast.literal_eval(line)
                    str_line = [str(item) for item in line]
                    for i in range(12):
                        self.pid_inputs[i].setText(str_line[i])
            # s = "print('Hello, World!')"
            # eval(s)

    def mecanum_setSpeedXYZ(self):  # 设置车体速度
        cmd_id = [0x40, 0x60]
        speedX = float(self.widget_mecanum.S_speedX.value())
        speedY = float(self.widget_mecanum.S_speedY.value())
        speedZ = float(self.widget_mecanum.S_speedZ.value())
        self.widget_mecanum.input_speedX.setText(str(speedX))
        self.widget_mecanum.input_speedY.setText(str(speedY))
        self.widget_mecanum.input_speedZ.setText(str(speedZ))
        speedX = floatToList_B(speedX)
        speedY = floatToList_B(speedY)
        speedZ = floatToList_B(speedZ)
        cmd_data = speedX + speedY + speedZ
        self.fuc_serial_send(cmd_id, cmd_data)

    def mecanum_setSpeedXYZ1(self):  # 设置车体速度
        cmd_id = [0x40, 0x60]
        speedX = float(self.widget_mecanum.input_speedX.text())
        speedY = float(self.widget_mecanum.input_speedY.text())
        speedZ = float(self.widget_mecanum.input_speedZ.text())
        self.widget_mecanum.S_speedX.setValue(int(speedX))
        self.widget_mecanum.S_speedY.setValue(int(speedY))
        self.widget_mecanum.S_speedZ.setValue(int(speedZ))
        speedX = floatToList_B(speedX)
        speedY = floatToList_B(speedY)
        speedZ = floatToList_B(speedZ)
        cmd_data = speedX + speedY + speedZ
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # AGV手柄
    def toggle_widget_handle(self):
        self.widget_handle.show()

    def handle_calibrate_begin(self):
        self.flag_calibrate_scale = 1
        self.widget_handle.line_scale_LXmin.setText(str(2048))
        self.widget_handle.line_scale_LXmax.setText(str(2048))
        self.widget_handle.line_scale_LYmin.setText(str(2048))
        self.widget_handle.line_scale_LYmax.setText(str(2048))
        self.widget_handle.line_scale_RXmin.setText(str(2048))
        self.widget_handle.line_scale_RXmax.setText(str(2048))
        self.widget_handle.line_scale_RYmin.setText(str(2048))
        self.widget_handle.line_scale_RYmax.setText(str(2048))
        cmd_id = [0x00, 0x00]
        cmd_data = []
        self.fuc_serial_send(cmd_id, cmd_data)

    def handle_calibrate_end(self):
        self.flag_calibrate_scale = 0
        lx = int16Tolist(int(self.widget_handle.line_scale_LX.text()))
        ly = int16Tolist(int(self.widget_handle.line_scale_LY.text()))
        rx = int16Tolist(int(self.widget_handle.line_scale_RX.text()))
        ry = int16Tolist(int(self.widget_handle.line_scale_RY.text()))
        cmd_id = [0x00, 0x01]
        cmd_data = lx + ly + rx + ry
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # 控制板测试
    def testBoard_test(self):
        cmd_id = [0x00, 0x10]
        cmd_data = [00]
        self.fuc_serial_send(cmd_id, cmd_data)
        self.widget_testBoard.label_2.setText('测试中')
        self.widget_testBoard.label_2.setStyleSheet("color: red; background-color: #ffebeb;")

    def testBoard_retest(self):
        for i in range(3):
            for j in range(8):
                self.widget_testBoard.IO[i*8+j].setChecked(False)
        for i in range(4):
            for j in range(2):
                self.widget_testBoard.communicate[i*2+j].setChecked(False)
        self.widget_testBoard.label_2.setText('测试中')

    def testBoard_testSelect(self):
        pass
    # endregion
    # region # 清洗机

    def fuc_pushRod(self, p1, p2):  # 推杆
        cmd_id = [0x50, 0x02]
        cmd_data = [p1, p2]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_water_valve(self):  # 清水阀
        cmd_id = [0x50, 0x03]
        if self.widget_clean.checkBox_water_valve.isChecked():
            cmd_data = [0x02, 0x01]
        else:
            cmd_data = [0x02, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_sewage_valve(self):  # 污水阀
        cmd_id = [0x50, 0x03]
        if self.widget_clean.checkBox_sewage_valve.isChecked():
            cmd_data = [0x03, 0x01]
        else:
            cmd_data = [0x03, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_scrub(self):  # 刷盘
        cmd_id = [0x50, 0x03]
        if self.widget_clean.checkBox_scrub.isChecked():
            cmd_data = [0x04, 0x01]
        else:
            cmd_data = [0x04, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def clean_vacuum_power(self):  # 抽真空电机供电
        cmd_id = [0x50, 0x03]
        if self.widget_clean.checkBox_vacuum.isChecked():
            cmd_data = [0x05, 0x01]
        else:
            cmd_data = [0x05, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_vacuum(self):  # 真空电机
        cmd_id = [0x50, 0x04]
        value = self.widget_clean.horizontalSlider_vacuum.value()
        cmd_data = [value]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_vacuum_input(self):  # 真空电机
        value = int(self.widget_clean.lineEdit_vacuum.text())
        self.widget_clean.horizontalSlider_vacuum.setValue(value)
        self.fuc_vacuum()

    def fuc_water_pump(self):  # 水泵
        cmd_id = [0x50, 0x03]
        value = self.widget_clean.horizontalSlider_water_pump.value()
        cmd_data = [0x01, value]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_water_pump_input(self):  # 水泵
        value = int(self.widget_clean.lineEdit_water_pump.text())
        self.widget_clean.horizontalSlider_water_pump.setValue(value)
        self.fuc_water_pump()

    def fuc_clean_move(self, p1):  # 运动控制
        cmd_id = [0x40, 0x60]
        lineSpeed = float(self.widget_clean.lineEdit_clean_lineSpeed.text())
        radSpeed = float(self.widget_clean.lineEdit_clean_radSpeed.text())
        if p1 == 0:
            lineSpeed = 0
            radSpeed = 0
        elif p1 == 1:
            lineSpeed = abs(lineSpeed)
            radSpeed = 0
        elif p1 == 2:
            lineSpeed = -abs(lineSpeed)
            radSpeed = 0
        elif p1 == 3:
            lineSpeed = 0
            radSpeed = abs(radSpeed)
        elif p1 == 4:
            lineSpeed = 0
            radSpeed = -abs(radSpeed)

        cmd_data = floatToList(lineSpeed) + floatToList(radSpeed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def clean_led_control(self):
        state = int(self.widget_clean.lineEdit_led_control.text())
        cmd_id = [0x40, 0x07]
        cmd_data = [state]
        self.fuc_serial_send(cmd_id, cmd_data)

    def clean_voice_control(self):
        state = int(self.widget_clean.lineEdit_voice_control.text())
        cmd_id = [0x40, 0x03]
        cmd_data = [state, 0xff, 0xff]
        self.fuc_serial_send(cmd_id, cmd_data)

    def clean_change_avoid_area(self):  # 切换雷达避障区域
        device = self.widget_clean.comboBox_avoid_device.currentIndex() + 1
        area = int(self.widget_clean.lineEdit_avoid_area.text())
        cmd_id = [0x40, 0x08]
        cmd_data = [device, area]
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # 清洗机工作站

    def clean_output_control(self, outputId):  # 输出控制
        if outputId == 0x01:
            cmd = self.widget_clean.checkBox_station_led_charge.isChecked()
        elif outputId == 0x02:
            cmd = self.widget_clean.checkBox_station_led_water.isChecked()
        elif outputId == 0x03:
            cmd = self.widget_clean.checkBox_station_led_water2.isChecked()
        elif outputId == 0x04:
            cmd = self.widget_clean.checkBox_station_led_clean.isChecked()
        elif outputId == 0x05:
            cmd = self.widget_clean.checkBox_station_led_green.isChecked()
        elif outputId == 0x06:
            cmd = self.widget_clean.checkBox_station_led_red.isChecked()
        elif outputId == 0x07:
            cmd = self.widget_clean.checkBox_station_led_yellow.isChecked()
        elif outputId == 0x08:
            cmd = self.widget_clean.checkBox_station_out_extend.isChecked()
        elif outputId == 0x09:
            cmd = self.widget_clean.checkBox_station_out_retract.isChecked()
        elif outputId == 0x0A:
            cmd = self.widget_clean.checkBox_station_valve_clean.isChecked()
        elif outputId == 0x0B:
            cmd = self.widget_clean.checkBox_station_valve_water.isChecked()
        if cmd:
            cmd = 0x01
        else:
            cmd = 0x00

        cmd_id = [0x41, 0x00]
        cmd_data = [outputId, cmd]
        self.fuc_serial_send(cmd_id, cmd_data)

    def cleanStation_charge(self):
        if self.widget_clean.checkBox_station_charge_2.isChecked():
            cmd_data = [0x01]
        else:
            cmd_data = [0x00]
        cmd_id = [0x41, 0x02]
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # CTU

    def ctu_motor_mode(self, motorId):  # 电机模式设置
        if motorId == 0x02:
            mode = self.widget_ctu_up.com_motor2_mode.currentText()
        elif motorId == 0x03:
            mode = self.widget_ctu_up.com_motor3_mode.currentText()
        elif motorId == 0x04:
            mode = self.widget_ctu_up.com_motor4_mode.currentText()
        elif motorId == 0x05:
            mode = self.widget_ctu_up.com_motor5_mode.currentText()
        mode = int(mode.split(':')[0])
        cmd_id = [0x40, 0x12]
        if mode < 0:
            mode = 0x100 + mode
        cmd_data = [motorId, mode]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_motor_setOrigin(self, motorId):  # 原点标定
        cmd_id = [0x40, 0x13]
        cmd_data = [motorId]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_motor_position_T(self, motorId):  # 电机运行到指定位置
        limitspeed = 3000
        if motorId == 0x02:
            speed = self.widget_ctu_up.input_motor2_speed_T.text()
            position = self.widget_ctu_up.input_motor2_position_T.text()
        elif motorId == 0x03:
            speed = self.widget_ctu_up.input_motor3_speed_T.text()
            position = self.widget_ctu_up.input_motor3_position_T.text()
        elif motorId == 0x04:
            speed = self.widget_ctu_up.input_motor4_speed_T.text()
            position = self.widget_ctu_up.input_motor4_position_T.text()
        elif motorId == 0x05:
            speed = self.widget_ctu_up.input_motor5_speed_T.text()
            position = self.widget_ctu_up.input_motor5_position_T.text()
        speed = int(speed)
        position = int(position)
        if speed > limitspeed:
            speed = limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(limitspeed))
        elif speed < -limitspeed:
            speed = -limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(-limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(-limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(-limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(-limitspeed))

        cmd_id = [0x40, 0x14]
        cmd_data = [motorId] + int32Tolist(position) + int32Tolist(speed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_device_location_T(self, motorId):  # 设备运行到指定位置
        # 获取设置参数
        if motorId == 0x02:
            location = self.widget_ctu_up.input_motor2_location_T.text()
            speed = self.widget_ctu_up.input_motor2_speed_T.text()
            position = self.widget_ctu_up.input_motor2_position_T.text()
        elif motorId == 0x03:
            location = self.widget_ctu_up.input_motor3_location_T.text()
            speed = self.widget_ctu_up.input_motor3_speed_T.text()
            position = self.widget_ctu_up.input_motor3_position_T.text()
        elif motorId == 0x04:
            location = self.widget_ctu_up.input_motor4_location_T.text()
            speed = self.widget_ctu_up.input_motor4_speed_T.text()
            position = self.widget_ctu_up.input_motor4_position_T.text()
        elif motorId == 0x05:
            location = self.widget_ctu_up.input_motor5_location_T.text()
            speed = self.widget_ctu_up.input_motor5_speed_T.text()
            position = self.widget_ctu_up.input_motor5_position_T.text()
        location = float(location)
        speed = int(speed)
        position = int(position)
        # 速度限制
        limitspeed = 3000
        if speed > limitspeed:
            speed = limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(limitspeed))
        elif speed < -limitspeed:
            speed = -limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(-limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(-limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(-limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(-limitspeed))
        # 位置限制
        if motorId == 0x02:
            position = location * 7213.93  # 上升1mm对应编码值递减7213.93，首层离地面411mm,层高为402mm
            position = clip_float(position, 0, 3000 * 7213.93)
        elif motorId == 0x03:
            position = -location / 90 * position
            position = clip_float(position, -3440000, 3440000)
        elif motorId == 0x04:
            # 伸到位1350mm,缩到位0mm,(1350-0)/2=675mm
            position = (location - 675) / 675 * position
            position = clip_float(position, -1239000, 1239000)
        elif motorId == 0x05:
            # 张开到位475mm,闭合到位328mm,(475-328)/2=74mm
            position = -(location - 401) / 74 * position
            position = clip_float(position, -161650, 161650)

        position = int(position)
        cmd_id = [0x40, 0x14]
        cmd_data = [motorId] + int32Tolist(position) + int32Tolist(speed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_motor_speed_T(self, motorId, btnId):  # 电机速度设置
        limitspeed = 3000
        if motorId == 0x02:
            speed = self.widget_ctu_up.input_motor2_speed_T.text()
        elif motorId == 0x03:
            speed = self.widget_ctu_up.input_motor3_speed_T.text()
        elif motorId == 0x04:
            speed = self.widget_ctu_up.input_motor4_speed_T.text()
        elif motorId == 0x05:
            speed = self.widget_ctu_up.input_motor5_speed_T.text()
        elif motorId == 0x06:
            speed = 0
        speed = int(speed)
        if speed > limitspeed:
            speed = limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(limitspeed))
        elif speed < -limitspeed:
            speed = -limitspeed
            if motorId == 0x02:
                self.widget_ctu_up.input_motor2_speed_T.setText(str(-limitspeed))
            elif motorId == 0x03:
                self.widget_ctu_up.input_motor3_speed_T.setText(str(-limitspeed))
            elif motorId == 0x04:
                self.widget_ctu_up.input_motor4_speed_T.setText(str(-limitspeed))
            elif motorId == 0x05:
                self.widget_ctu_up.input_motor5_speed_T.setText(str(-limitspeed))
        if btnId == 0x01:
            speed = abs(speed)
        elif btnId == 0x02:
            speed = -abs(speed)
        elif btnId == 0x00:
            speed = 0

        cmd_id = [0x40, 0x15]
        cmd_data = [motorId] + int32Tolist(speed)
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_motor_control(self, motorId, btnId):  # 电机一键控制
        if motorId == 0x02:
            mode = self.widget_ctu_up.com_motor2_mode.currentText()
        elif motorId == 0x03:
            mode = self.widget_ctu_up.com_motor3_mode.currentText()
        elif motorId == 0x04:
            mode = self.widget_ctu_up.com_motor4_mode.currentText()
        elif motorId == 0x05:
            mode = self.widget_ctu_up.com_motor5_mode.currentText()
        mode = int(mode.split(':')[0])

        if mode == 0x01 and btnId != 0x00:  # 位置模式
            if motorId == 0x02:
                speed = self.widget_ctu_up.input_motor2_speed_T.text()
                position = self.widget_ctu_up.input_motor2_position.text()
            elif motorId == 0x03:
                speed = self.widget_ctu_up.input_motor3_speed_T.text()
                position = self.widget_ctu_up.input_motor3_position_T.text()
            elif motorId == 0x04:
                speed = self.widget_ctu_up.input_motor4_speed_T.text()
                position = self.widget_ctu_up.input_motor4_position_T.text()
            elif motorId == 0x05:
                speed = self.widget_ctu_up.input_motor5_speed_T.text()
                position = self.widget_ctu_up.input_motor5_position_T.text()
            speed = int(speed)
            if motorId == 0x02:  # 升降机构
                if (btnId == 0x01):
                    height = abs(int(self.widget_ctu_up.input_motor2_location_T.text()))
                    position = int(position) + int(height * 7213.93)  # 上升1mm对应编码值递减7213.93
                elif (btnId == 0x02):
                    height = abs(int(self.widget_ctu_up.input_motor2_location_T.text()))
                    position = int(position) - int(height * 7213.93)  # 上升1mm对应编码值递减7213.93
            else:  # 其它机构
                if (btnId == 0x01):
                    position = int(position)
                elif (btnId == 0x02):
                    position = -int(position)
            # 速度限制
            limitspeed = 3000
            if speed > limitspeed:
                speed = limitspeed
                if motorId == 0x02:
                    self.widget_ctu_up.input_motor2_speed_T.setText(str(limitspeed))
                elif motorId == 0x03:
                    self.widget_ctu_up.input_motor3_speed_T.setText(str(limitspeed))
                elif motorId == 0x04:
                    self.widget_ctu_up.input_motor4_speed_T.setText(str(limitspeed))
                elif motorId == 0x05:
                    self.widget_ctu_up.input_motor5_speed_T.setText(str(limitspeed))
            elif speed < -limitspeed:
                speed = -limitspeed
                if motorId == 0x02:
                    self.widget_ctu_up.input_motor2_speed_T.setText(str(-limitspeed))
                elif motorId == 0x03:
                    self.widget_ctu_up.input_motor3_speed_T.setText(str(-limitspeed))
                elif motorId == 0x04:
                    self.widget_ctu_up.input_motor4_speed_T.setText(str(-limitspeed))
                elif motorId == 0x05:
                    self.widget_ctu_up.input_motor5_speed_T.setText(str(-limitspeed))

            # 位置限制
            if motorId == 0x02:
                position = clip_float(position, 0, 3000 * 7213.93)
            elif motorId == 0x03:
                position = clip_float(position, -3440000, 3440000)
            elif motorId == 0x04:
                position = clip_float(position, -1239000, 1239000)
            elif motorId == 0x05:
                position = clip_float(position, -161650, 161650)

            cmd_id = [0x40, 0x14]
            cmd_data = [motorId] + int32Tolist(position) + int32Tolist(speed)
            self.fuc_serial_send(cmd_id, cmd_data)
        else:
            self.ctu_motor_speed_T(motorId, btnId)

    def ctu_motor_en(self, motorId):
        if motorId == 0x02:
            en = self.widget_ctu_up.checkBox_en_motor2.isChecked()
        elif motorId == 0x03:
            en = self.widget_ctu_up.checkBox_en_motor3.isChecked()
        elif motorId == 0x04:
            en = self.widget_ctu_up.checkBox_en_motor4.isChecked()
        elif motorId == 0x05:
            en = self.widget_ctu_up.checkBox_en_motor5.isChecked()
        if en:
            en = 0x01
        else:
            en = 0x00
        cmd_id = [0x40, 0x11]
        cmd_data = [motorId, en]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_output_control(self, outputId):  # 输出控制
        if outputId == 0x01:
            cmd = self.widget_ctu_up.checkBox_out_liftPWR.isChecked()
        elif outputId == 0x02:
            cmd = self.widget_ctu_up.checkBox_out_walkPWR.isChecked()
        elif outputId == 0x03:
            cmd = self.widget_ctu_up.checkBox_out_radarF1.isChecked()
        elif outputId == 0x04:
            cmd = self.widget_ctu_up.checkBox_out_radarF2.isChecked()
        elif outputId == 0x05:
            cmd = self.widget_ctu_up.checkBox_out_radarF3.isChecked()
        elif outputId == 0x06:
            cmd = self.widget_ctu_up.checkBox_out_radarF4.isChecked()
        elif outputId == 0x07:
            cmd = self.widget_ctu_up.checkBox_out_radarB1.isChecked()
        elif outputId == 0x08:
            cmd = self.widget_ctu_up.checkBox_out_radarB2.isChecked()
        elif outputId == 0x09:
            cmd = self.widget_ctu_up.checkBox_out_radarB3.isChecked()
        elif outputId == 0x0A:
            cmd = self.widget_ctu_up.checkBox_out_radarB4.isChecked()
        elif outputId == 0x0B:
            cmd = self.widget_ctu_up.checkBox_out_sto.isChecked()
        elif outputId == 0x0D:
            cmd = self.widget_ctu_up.checkBox_out_ledLR.isChecked()
        elif outputId == 0x0E:
            cmd = self.widget_ctu_up.checkBox_out_ledLG.isChecked()
        elif outputId == 0x0F:
            cmd = self.widget_ctu_up.checkBox_out_ledLY.isChecked()
        elif outputId == 0x10:
            cmd = self.widget_ctu_up.checkBox_out_ledRR.isChecked()
        elif outputId == 0x11:
            cmd = self.widget_ctu_up.checkBox_out_ledRG.isChecked()
        elif outputId == 0x12:
            cmd = self.widget_ctu_up.checkBox_out_ledRY.isChecked()

        if cmd:
            cmd = 0x01
        else:
            cmd = 0x00

        cmd_id = [0x41, 0x00]
        cmd_data = [outputId, cmd]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_device_control(self, deviceId):  # 设备控制
        if deviceId == 0x06:
            cmd = self.widget_ctu_up.checkBox_paddle.isChecked()
        if cmd:
            cmd = 0x01
        else:
            cmd = 0x00
        cmd_id = [0x41, 0x01]
        cmd_data = [deviceId, cmd]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_excecute_goods(self):  # 组合控制
        action_type = self.widget_ctu_up.comboBox_action_type.currentIndex() + 1
        location_pick = self.widget_ctu_up.comboBox_location_pick.currentIndex() + 1
        floor_pick = int(self.widget_ctu_up.lineEdit_floor_pick.text())
        location_place = self.widget_ctu_up.comboBox_location_place.currentIndex() + 1
        floor_place = int(self.widget_ctu_up.lineEdit_floor_place.text())
        if floor_pick < 0 or floor_place < 0:  # 层数小于0
            return
        elif location_pick == 1 and floor_pick > 5:  # 取货背篓层数超出
            return
        elif location_place == 1 and floor_place > 5:  # 放货背篓层数超出
            return
        elif (location_pick == 2 or location_pick == 3) and floor_pick > 23:  # 取货货架层数超出
            return
        elif (location_place == 2 or location_place == 3) and floor_place > 23:  # 放货货架层数超出
            return
        cmd_id = [0x42, 0x01]
        cmd_data = [action_type, location_pick, floor_pick, location_place, floor_place]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_excecute_stop(self):  # 清空任务队列
        cmd_id = [0x42, 0x02]
        cmd_data = []
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_emergencyStop(self):  # 模拟急停按键
        cmd_id = [0x40, 0x61]
        cmd_data = [0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_reset(self):  # 模拟复位按键
        cmd_id = [0x40, 0x61]
        cmd_data = [0x02]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_force(self, en):  # 是否强制手自动切换(忽略物理手自动按钮)
        cmd_id = [0x40, 0x61]
        if en:
            if self.widget_ctu_up.checkBox_manual_auto.isChecked():
                cmd_data = [0x03]  # 强制为自动模式
            else:
                cmd_data = [0x04]  # 强制为手动模式
        else:
            cmd_data = [0x05]
        self.fuc_serial_send(cmd_id, cmd_data)

    def ctu_manual_auto(self, mode):  # 手自动切换选择
        if self.widget_ctu_up.checkBox_force.isChecked():
            cmd_id = [0x40, 0x61]
            if self.widget_ctu_up.checkBox_manual_auto.isChecked():
                cmd_data = [0x03]  # 自动
            else:
                cmd_data = [0x04]  # 手动
            self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # LED

    def led_num(self):
        num = int(self.widget_leds.lineEdit_led_num.text())
        cmd_id = [0x91, 0x01]
        cmd_data = [0x00, num]
        self.fuc_serial_send(cmd_id, cmd_data)

    def led_mode(self):
        mode = self.widget_leds.comboBox_led_mode.currentIndex()
        cmd_id = [0x91, 0x01]
        cmd_data = [0x01, mode]
        self.fuc_serial_send(cmd_id, cmd_data)

    def led_color(self):
        color = self.widget_leds.comboBox_led_color.currentIndex()
        cmd_id = [0x91, 0x01]
        cmd_data = [0x02, color]
        self.fuc_serial_send(cmd_id, cmd_data)

    def led_light(self):
        light = self.widget_leds.horizontalSlider_led_light.value()
        self.widget_leds.lineEdit_led_light.setText(str(light))
        cmd_id = [0x91, 0x01]
        cmd_data = [0x03, light]
        self.fuc_serial_send(cmd_id, cmd_data)

    def led_light_input(self):
        light = int(self.widget_leds.lineEdit_led_light.text())
        self.widget_leds.horizontalSlider_led_light.setValue(light)
        self.led_light()

    def led_speed(self):
        speed = self.widget_leds.horizontalSlider_led_speed.value()
        self.widget_leds.lineEdit_led_speed.setText(str(speed))
        cmd_id = [0x91, 0x01]
        cmd_data = [0x04, speed]
        self.fuc_serial_send(cmd_id, cmd_data)

    def led_speed_input(self):
        speed = int(self.widget_leds.lineEdit_led_speed.text())
        self.widget_leds.horizontalSlider_led_speed.setValue(speed)
        self.led_speed()

    # endregion
    # region # 加密解密
    def pad(self, text):
        while len(text) % 16 != 0:
            text += self.PADDING
        return text

    def unpad(self, padded_text):
        return padded_text.rstrip(self.PADDING)

    def encrypt(self, message, key):
        # 生成随机初始化向量 (IV)
        iv = get_random_bytes(16)

        # 对明文进行填充
        cipher_text = self.pad(message.encode('utf-8'))

        # 创建AES对象并加密
        cipher = AES.new(key, self.MODE, iv)
        encrypted_text = cipher.encrypt(cipher_text)

        # 将IV和加密后的数据一起返回
        return base64.b64encode(iv + encrypted_text)

    def decrypt(self, ciphertext_base64, key):
        # 解码Base64编码的密文
        ciphertext = base64.b64decode(ciphertext_base64)

        # 分离出IV和加密文本
        iv = ciphertext[:16]
        cipher_text = ciphertext[16:]

        # 创建AES对象并解密
        cipher = AES.new(key, self.MODE, iv)
        decrypted_text = cipher.decrypt(cipher_text)

        # 去除填充内容
        original_message = self.unpad(decrypted_text).decode('utf-8')

        return original_message

    def fuc_query_uid(self):
        cmd_id = [0x10, 0x00]
        cmd_data = [0x04, 0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_generate_lic(self):
        rawlic = self.widget_license.input_license_xid1.text() + self.widget_license.input_license_xid2.text() + \
            self.widget_license.input_license_xid3.text() + self.widget_license.input_license_xdate.text()
        # 定义一个密钥（必须为16、24或32字节长）
        self.key = get_random_bytes(16)
        # 加密消息
        lic = self.encrypt(rawlic, self.key)
        self.widget_license.input_license.setText(lic.decode('utf-8'))

    def fuc_authorization(self):
        str_xid1 = self.widget_license.input_license_xid1.text()
        str_xid2 = self.widget_license.input_license_xid2.text()
        str_xid3 = self.widget_license.input_license_xid3.text()
        int_xid1 = listToUint32(bytes.fromhex(str_xid1)) - 0xB9FEC0FB
        int_xid2 = listToUint32(bytes.fromhex(str_xid2)) - 0xC2B7D1C7
        int_xid3 = listToUint32(bytes.fromhex(str_xid3)) - 0x686C6C79
        if int_xid1 < 0:
            int_xid1 += 0x100000000
        if int_xid2 < 0:
            int_xid2 += 0x100000000
        if int_xid3 < 0:
            int_xid3 += 0x100000000
        xid1 = int_xid1.to_bytes(4, byteorder='big')
        xid2 = int_xid2.to_bytes(4, byteorder='big')
        xid3 = int_xid3.to_bytes(4, byteorder='big')
        # self.widget_license.input_license.setText(xid1.hex()+''+xid2.hex()+''+xid3.hex())

        # 解密消息
        decrypted = self.decrypt(self.widget_license.input_license.text().encode('utf-8'), self.key)
        print("Decrypted: ", decrypted)
        self.widget_license.input_license.setText(decrypted)
        print(bytes.fromhex(decrypted))
        cmd_id = [0x10, 0x00]
        xid = [b for b in bytes.fromhex(decrypted)]
        xid1 = xid[0:4][::-1]
        xid2 = xid[4:8][::-1]
        xid3 = xid[8:12][::-1]
        xdate = xid[12:16][::-1]
        cmd_data = [0x07] + xid1 + xid2 + xid3 + xdate
        print(cmd_data)
        self.fuc_serial_send(cmd_id, cmd_data)

    def on_date_clicked(self, date):
        print("hlly")
        str_date = date.toString("yyMMdd")
        self.widget_license.input_license_date.setText(str_date)
        str_date = hex(date.year() - 2000)[2:].zfill(2) + hex(date.month())[2:].zfill(2) + hex(date.day())[2:].zfill(2)
        print(str_date)
        int_xdate = listToUint32(bytes.fromhex(str_date)) + 0x686C6C79
        self.widget_license.input_license_xdate.setText(bytes(int32Tolist_big(int_xdate)).hex())

    # endregion
    # region # 语音播报
    def fuc_voice_play(self):
        cmd_id = [0x40, 0x03]
        voice = self.widget_audio.com_voiceList.currentIndex() + 1
        cmd_data = [voice, 0xff, 0xff]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_voice_addFile(self):
        print("hlly")
        filename, _ = QFileDialog.getOpenFileName(None, 'Open File', r'E:\project\06 标准机开发\4、调试\语音素材包')
        dirname = os.path.dirname(filename)
        filename = os.path.basename(filename)
        self.root_mp3_files = dirname
        self.widget_audio.list_voice.addItem(filename)

    def fuc_voice_addFiles(self):
        filePath = self.widget_audio.input_voice_filePath.text()
        for root, dirs, files in os.walk(filePath):
            self.root_mp3_files = root
            for file in files:
                if file.endswith('.mp3'):
                    self.widget_audio.list_voice.addItem(file)
        # self.listModel = QStringListModel()
        # self.listModel.setStringList(self.mp3_files)
        # self.listModel.sort(0, Qt.AscendingOrder)#DescendingOrder
        # self.widget_audio.list_voice.setModel(self.listModel)
        # self.mp3_files.append(os.path.join(root, file))

    def fuc_voice_moveUp(self):
        current_row = self.widget_audio.list_voice.currentRow()
        # current_item=self.widget_audio.list_voice.item(current_row)
        if current_row > 0:
            item_above = self.widget_audio.list_voice.takeItem(current_row - 1)  # 取出上一行的条目
            self.widget_audio.list_voice.insertItem(current_row, item_above)  # 将原本上一行的条目放到当前条目的下方

    def fuc_voice_moveDown(self):
        current_row = self.widget_audio.list_voice.currentRow()
        if current_row < self.widget_audio.list_voice.count() - 1:
            item_below = self.widget_audio.list_voice.takeItem(current_row + 1)
            self.widget_audio.list_voice.insertItem(current_row, item_below)

    def fuc_voice_delFile(self):
        fileindex = self.widget_audio.list_voice.currentRow()
        self.widget_audio.list_voice.takeItem(fileindex)

    def fuc_voice_delFiles(self):
        self.widget_audio.list_voice.clear()

    def fuc_voice_downloadSel(self):  # 下载选中的音频
        self.flag_voice_download = 1
        self.widget_audio.progress_voice_file.setMaximum(1)
        fileindex = self.widget_audio.list_voice.currentRow() + 1
        item_text = self.widget_audio.list_voice.currentItem().text()
        filename = (os.path.join(self.root_mp3_files, item_text))
        self.thread_sendAudioFile = thread_sendAudioFile(filename, fileindex, 0)
        self.thread_sendAudioFile.sig_sendAudioFile.connect(self.fuc_voice_sendFile)
        self.thread_sendAudioFile.sig_log.connect(self.fuc_log)
        self.thread_sendAudioFile.sig_packNum.connect(self.fuc_voice_downloadCnt)
        self.thread_sendAudioFile.sig_done.connect(self.fuc_voice_downloadOnce)
        self.thread_sendAudioFile.start()

    def fuc_voice_downloadCnt(self, packNum):
        self.widget_audio.progress_voice_pack.setMaximum(packNum)

    def fuc_voice_downloadFeedback(self, data):
        if len(data) == 7:
            if data[0] == 0x7E and data[1] == 0xA0 and data[6] == 0xEF:
                self.widget_audio.progress_voice_pack.setValue(data[4] * 256 + data[5])
        elif len(data) > 7 and data[0] == 0x7E and data[1] == 0xA0 and data[6] == 0xEF:
            self.widget_audio.progress_voice_pack.setValue(data[4] * 256 + data[5])
        else:
            a = HexStrAddSpace(data.hex())
            self.fuc_log(a)

    def fuc_voice_downloadDone(self):  # 音频下载完成后
        self.fuc_log("下载完成")
        self.flag_voice_download = 0
        time.sleep(0.1)
        sendData = [0x7E, 0xA3, 0x00, 0x02, 0x00, 0x00, 0xEF]
        self.fuc_serial_sendRaw(sendData)

    def fuc_voice_downloadOnce(self):  # 选中的音频下载完成后
        progress = self.widget_audio.progress_voice_pack.value()
        progress_total = self.widget_audio.progress_voice_pack.maximum()
        print(progress)
        print(progress_total)
        if progress == progress_total:
            self.fuc_voice_downloadDone()
        else:
            print("下载失败")
            self.fuc_log("下载失败")
        self.widget_audio.progress_voice_file.setValue(1)

    def fuc_voice_downloadNext(self):  # 下载下一个音频
        progress = self.widget_audio.progress_voice_pack.value()
        progress_total = self.widget_audio.progress_voice_pack.maximum()
        print(progress)
        print(progress_total)
        if progress >= progress_total - 1:
            self.list_voice_index += 1
            self.widget_audio.progress_voice_file.setValue(self.list_voice_index)
            if self.list_voice_index < self.list_voice_num:
                item_text = self.widget_audio.list_voice.item(self.list_voice_index).text()
                filename = (os.path.join(self.root_mp3_files, item_text))
                self.thread_sendAudioFile = thread_sendAudioFile(filename, self.list_voice_index + 1, 1)
                self.thread_sendAudioFile.sig_sendAudioFile.connect(self.fuc_voice_sendFile)
                self.thread_sendAudioFile.sig_log.connect(self.fuc_log)
                self.thread_sendAudioFile.sig_packNum.connect(self.fuc_voice_downloadCnt)
                self.thread_sendAudioFile.sig_next.connect(self.fuc_voice_downloadNext)
                self.thread_sendAudioFile.start()
            else:
                self.fuc_voice_downloadDone()
        else:
            print("下载失败")
            self.fuc_log("下载失败")

    def fuc_voice_downloadAll(self):  # 下载所有的音频
        self.flag_voice_download = 1
        self.list_voice_num = self.widget_audio.list_voice.count()
        self.widget_audio.progress_voice_file.setMaximum(self.list_voice_num)
        self.widget_audio.progress_voice_file.setValue(0)
        self.list_voice_index = 0
        item_text = self.widget_audio.list_voice.item(0).text()
        filename = (os.path.join(self.root_mp3_files, item_text))
        self.thread_sendAudioFile = thread_sendAudioFile(filename, self.list_voice_index + 1, 1)
        self.thread_sendAudioFile.sig_sendAudioFile.connect(self.fuc_voice_sendFile)
        self.thread_sendAudioFile.sig_log.connect(self.fuc_log)
        self.thread_sendAudioFile.sig_packNum.connect(self.fuc_voice_downloadCnt)
        self.thread_sendAudioFile.sig_next.connect(self.fuc_voice_downloadNext)
        self.thread_sendAudioFile.start()

    def fuc_voice_sendFile(self, sendData):
        result = self.fuc_serial_sendRaw(sendData)
        if (result == 0):
            self.thread_sendAudioFile.stop_send()

    def fuc_voice_update(self):
        cmd_id = [0x40, 0x02]
        cmd_data = [0x01]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_voice_exit(self):
        sendData = [0x7E, 0xA3, 0x00, 0x02, 0x00, 0x00, 0xEF]
        result = self.fuc_serial_sendRaw(sendData)
        # print(result)
        self.flag_voice_download = 0
        time.sleep(0.1)
        sendData = [0xAA, 0xAA, 0xAA]
        result = self.fuc_serial_sendRaw(sendData)

    def fuc_voice_setVolume(self):
        cmd_id = [0x40, 0x03]
        volume = self.slider_volume.value()
        cmd_data = [0xff, 0xff, volume]
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # 输入输出
    def fuc_outputs_control(self):
        s = self.widget_IO.lineEdit_outputs_control.text()
        hex_array = []
        for i in range(0, len(s), 2):
            decimal_value = int(s[i:i + 2])
            hex_value = decimal_value & 0xff  # 确保是单字节
            hex_array.append(hex_value)
        cmd_id = [0x40, 0x05]
        cmd_data = hex_array
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_outputGroupControl(self, num):
        if self.widget_IO.radioButton_output.isChecked():
            sender = self.sender()
            state = sender.isChecked()
            cmd_id = [0x40, 0x04]
            cmd_data = [num, state]
            self.fuc_serial_send(cmd_id, cmd_data)
        elif self.widget_IO.radioButton_outputGroup.isChecked():
            out = [0x00] * 3
            out[0] = self.widget_IO.check_out_1.isChecked() << 7 | self.widget_IO.check_out_2.isChecked() << 6 | self.widget_IO.check_out_3.isChecked(
            ) << 5 | self.widget_IO.check_out_4.isChecked() << 4 | self.widget_IO.check_out_5.isChecked() << 3 | self.widget_IO.check_out_6.isChecked(
            ) << 2 | self.widget_IO.check_out_7.isChecked() << 1 | self.widget_IO.check_out_8.isChecked()
            out[1] = self.widget_IO.check_out_9.isChecked() << 7 | self.widget_IO.check_out_10.isChecked() << 6 | self.widget_IO.check_out_11.isChecked(
            ) << 5 | self.widget_IO.check_out_12.isChecked() << 4 | self.widget_IO.check_out_13.isChecked() << 3 | self.widget_IO.check_out_14.isChecked(
            ) << 2 | self.widget_IO.check_out_15.isChecked() << 1 | self.widget_IO.check_out_16.isChecked()
            out[2] = self.widget_IO.check_out_17.isChecked() << 7 | self.widget_IO.check_out_18.isChecked() << 6 | self.widget_IO.check_out_19.isChecked(
            ) << 5 | self.widget_IO.check_out_20.isChecked() << 4 | self.widget_IO.check_out_21.isChecked() << 3 | self.widget_IO.check_out_22.isChecked(
            ) << 2 | self.widget_IO.check_out_23.isChecked() << 1 | self.widget_IO.check_out_24.isChecked()
            # str_out=[hex(i) for i in out]
            # print(str_out)
            cmd_id = [0x40, 0x06]
            cmd_data = out
            self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_led_control(self):
        state = int(self.widget_IO.lineEdit_led_control.text())
        cmd_id = [0x40, 0x07]
        cmd_data = [state]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_interface_heartbeat(self):
        time = int(self.widget_IO.lineEdit_interface_heartbeat.text())
        cmd_id = [0x40, 0x01]
        cmd_data = [time >> 8 & 0xff, time & 0xff]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_change_avoid_area(self):
        device = self.widget_IO.comboBox_avoid_device.currentIndex() + 1
        area = int(self.widget_IO.lineEdit_avoid_area.text())
        cmd_id = [0x40, 0x08]
        cmd_data = [device, area]
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # 清洗机驱动板测试

    def fuc_driver_pwm(self):
        num = self.widget_clean_driver.com_driver_output.currentIndex() + 1
        pwm = self.widget_clean_driver.slider_driver_pwm.value()
        self.widget_clean_driver.lineEdit_driver_pwm.setText(str(pwm))
        cmd_id = [0x42, 0x00]
        cmd_data = [num, pwm]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_driver_speed(self):
        num = self.widget_clean_driver.com_driver_motor.currentIndex() + 1
        dir = self.widget_clean_driver.com_driver_dir.currentIndex()
        if dir == 0:
            dir = 0x01
        elif dir == 1:
            dir = 0x02
        else:
            dir = 0x00
        speed = self.widget_clean_driver.slider_driver_speed.value()
        self.widget_clean_driver.lineEdit_driver_speed.setText(str(speed))
        cmd_id = [0x42, 0x01]
        cmd_data = [num, dir, speed]
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # 程序监控
    def fuc_print_threadName(self):
        cmd_id = [0x80, 0x03]
        if self.widget_license.checkBox_print_threadName.isChecked():
            cmd_data = [0x01]
        else:
            cmd_data = [0x00]
        self.fuc_serial_send(cmd_id, cmd_data)

    def fuc_query_softwareVersion(self):
        cmd_id = [0x80, 0x04]
        cmd_data = []
        self.fuc_serial_send(cmd_id, cmd_data)

    # endregion
    # region # 上位机启动
    def restart(self):
        self.close()
        qApp.exit(current_exit_code)

    def showChild(self):  # 上位机启动时显示子窗口
        if self.widget_config.p_isShowMecanum.isChecked():
            self.widget_mecanum.show()
        if self.widget_config.p_isShowHandle.isChecked():
            self.widget_handle.show()
        if self.widget_config.p_isShowDebug.isChecked():
            self.window_debug.gridLayout.addWidget(self.paint)
            self.paint.setMinimumWidth(500)  # 设置widget最小宽度
            self.window_debug.show()

    # endregion
    # region # 临时测试
    def w_test_begin(self):
        print("hlly开始测试")
        self.ctuTest_floor_current = int(self.w_test.lineEdit_floor_begin.text())
        self.thread_test.flag_run = True
        self.thread_test.start()

    def w_test_end(self):
        self.thread_test.stop()
        self.ctu_excecute_stop()  # 下发到CTU清空后续任务队列
        print("hlly结束测试")

    def w_test_change_time_interval(self):  # 设置间隔时间
        time_interval = float(self.w_test.lineEdit_time_interval.text())
        if time_interval > 7:
            time_interval = 7
        self.thread_test.time_interval = time_interval

    def w_test_update_text(self, txt):  # 触发更新
        self.fuc_log(txt)
        floor_begin = int(self.w_test.lineEdit_floor_begin.text())
        floor_end = int(self.w_test.lineEdit_floor_end.text())
        if self.ctuTest_floor_current <= floor_end:
            self.widget_ctu_up.lineEdit_floor_pick.setText(str(self.ctuTest_floor_current))
            self.ctu_excecute_goods()
        else:
            self.ctuTest_floor_current = floor_begin
            self.widget_ctu_up.lineEdit_floor_pick.setText(str(self.ctuTest_floor_current))
            self.ctu_excecute_goods()
        self.ctuTest_floor_current += 1
    # endregion


if __name__ == '__main__':
    # 启用高DPI支持
    QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)  # 适应高分辨率
    QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
    current_exit_code = 1002457844
    app = QApplication(sys.argv)
    try:
        import ctypes
        ctypes.windll.shcore.SetProcessDpiAwareness(2)  # 2表示PerMonitorV2模式
    except:
        pass
    # app.setStyle('Fusion')  # 设置 Fusion 风格
    # 获取当前 exe 所在文件夹路径
    parent_dir_path = get_exe_directory()
    # parent_dir_path = os.path.dirname(get_exe_directory())
    os.chdir(parent_dir_path)
    # current_directory = get_exe_directory()
    flag_recordTime = False
    RecordTime = get_time()

    # widget_picture = window_picture()
    # widget_picture.show()
    # sys.exit(app.exec())
    while True:
        main_window = Window_main()
        # widget_picture.hide()
        # style_file = r'E:\python\上位机_舜宇贝尔\AGV-Develop_V3.4\QSS/myQSS.qss'
        # style_sheet = QSSLoader.read_qss_file(style_file)
        # print(style_sheet)

        # app.setStyleSheet(qdarkstyle.load_stylesheet(qt_api='pyqt5',palette=LightPalette()))#灰色主题
        # app.setStyleSheet(qdarkstyle.load_stylesheet(qt_api='pyqt5'))#暗黑色主题

        main_window.show()
        end_time = time.time()
        elapsed_time = end_time - start_time
        print("窗口加载时间：", elapsed_time, "秒")
        main_window.showChild()
        code = app.exec()
        if code != current_exit_code:
            if global_flag_client == 1:
                global_flag_client = 0
                global_s.close()
                print("socket已断开")
            break
    print("程序关闭")
    sys.exit(0)
